This pipeline is intended for use with three levels of proteomics
data: total proteome, phosphoproteome, and histone proteome. See Schaefer et al. (2024) for more detail on the
total and histone proteome; phosphoproteome data is from Choudhary et al. (2020). Total and
phosphoproteome data are designed to be input as an Excel sheet from
Proteome Discoverer, and the histone data are the aggregated ratios from
EpiProfile (Yuan et al. (2018)).
For the reader, code chunks are collapsible in-text. The full R
Markdown source document can also be downloaded from this page by
clicking the “Code” dropdown above. Additionally, we have made available
the files generated from our data at the project repository on Github.
Other files can be downloaded from their respective sources.
Proteome data
processing
This section is designed to work with normalized abundance data from
Proteome Discoverer. The total_path variable should point
directly to the Excel file containing the data you would like to use.
The range identified in the read_xlsx command should
contain columns from “Accession” to the final “Found in Sample:”
column.
# Read in normalized abundance data starting with Accession column
total_path <- "~/Documents/R/33060_Control_vs_Infection_JMI_abundances_normalizedabundances.xlsx"
total_KB <- read_xlsx(path = total_path, range = cell_cols(c("D:BI")))
total_KB <- select(
total_KB,
c(
"Accession",
starts_with(
c("Abundance Ratio (log2):", "Abundance Ratio Adj. P-Value:", "Found in Sample:")
)
)
)
# Retrieve keys and assign symbol to numerical Entrez ID
# (KEGG mapping gives an error if this isn't included)
# Example: given Entrez ID 3043, assign Uniprot accession number P68871,
# then map the gene symbol HBB for easier interpretation later.
reflist <- OrganismDbi::select(
Homo.sapiens,
keys = keys(Homo.sapiens, keytype = "UNIPROT"),
columns = c("ENTREZID", "SYMBOL"), keytype = "UNIPROT"
)
total_KB <- full_join(total_KB, reflist, by = join_by(Accession == UNIPROT))
# Getting rid of this huge variable to save space
rm(reflist)
# Drop invalid data (NA), drop any duplicate cases, rename columns for clarity
total_KB <- total_KB %>%
drop_na(SYMBOL) %>%
distinct(SYMBOL, .keep_all = TRUE) %>%
rename(
"log2(FC)" = starts_with("Abundance Ratio (log2)"),
"P value" = starts_with("Abundance Ratio Adj. P-Value")
)
# Remove points with invalid P values (horizontal line at the top of a plot),
# assign symbols to row names
total_KB <- total_KB %>%
filter(`P value` > 5.3e-16) %>%
mutate("SYMBOL2" = `SYMBOL`) %>%
column_to_rownames(var = "SYMBOL")
# Set threshold values for proteome analysis
total_p_threshold <- 0.05
total_FC_threshold <- 0.5
# Subset the data for plotting later - anything with P < threshold
total_significant <- filter(total_KB, `P value` < total_p_threshold)
Volcano
Figure 1.1a is a general volcano plot, showing a visualization of the
full dataset.
The max.overlaps parameter in the
geom_text_repel call should be carefully adjusted. The plot
may have to be generated multiple times to get an appropriate labeling
count. Setting this value to Inf is possible, but will
likely overwhelm the plot and no points will be visible.
# Generating overall volcano plot
# Setting categories for differential expression:
# not significant, P-value only, FC only, both
total_KB$DiffExp <- case_when(
abs(total_KB$`log2(FC)`) >= total_FC_threshold &
total_KB$`P value` > total_p_threshold ~ "FC only",
abs(total_KB$`log2(FC)`) < total_FC_threshold &
total_KB$`P value` <= total_p_threshold ~ "P val. only",
abs(total_KB$`log2(FC)`) >= total_FC_threshold &
total_KB$`P value` <= total_p_threshold ~ "FC and P val.",
.default = "NS"
)
total_KB$DiffExp <- factor(total_KB$DiffExp,
levels = c("FC and P val.", "P val. only", "FC only", "NS")
)
# Setting alpha values and labels so the significant points are most visible
total_KB$DEAlpha <- 0.3
total_KB$DEAlpha[abs(total_KB$`log2(FC)`) >= total_FC_threshold &
total_KB$`P value` <= total_p_threshold] <- 0.8
total_KB$labels <- if_else(
abs(total_KB$`log2(FC)`) >= total_FC_threshold &
total_KB$`P value` <= total_p_threshold, total_KB$SYMBOL2, ""
)
ggplot(total_KB, aes(x = `log2(FC)`, y = -log10(`P value`))) +
geom_point(aes(color = DiffExp, alpha = DEAlpha)) +
geom_vline(
xintercept = c(total_FC_threshold, -total_FC_threshold),
alpha = 0.5, linetype = "dashed"
) +
geom_hline(
yintercept = -log10(total_p_threshold),
alpha = 0.5, linetype = "dashed"
) +
geom_text_repel(aes(label = labels), show.legend = FALSE, size = rel(3)) +
guides(alpha = "none") +
labs(
x = "log2(Fold Change)", y = "-log10(P value)",
title = "Figure 1.1a: Volcano plot",
color = "Category"
) +
theme(legend.position = "bottom") +
scale_fill_viridis_d(
aesthetics = "color", option = "magma",
begin = 0.2, end = 0.9
)
## Warning: ggrepel: 239 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

If there are known genes of interest to be included in a plot, Figure
1.1b plots these points on top of the full dataset as a background.
# Defining known genes of interest for a second plot
Group1 <- c(
"MORF4L1", "SAMSN1", "DTX3L", "MBD3", "HSF1", "EYA3",
"NCAPD2", "TAF9", "PPP4C", "ZNF638"
)
Group2 <- c("CD276", "CDK6", "CD9", "CD82", "NFKB1", "BST2", "CTSH")
# Assigning shapes and labels for each group in the volcano plot,
# leaving points unlabeled if outside the groups
total_KB$CatShapes <- case_when(
total_KB$SYMBOL2 %in% Group1 ~ "1",
total_KB$SYMBOL2 %in% Group2 ~ "2",
.default = "20"
)
total_KB$CatNames <- case_when(
total_KB$SYMBOL2 %in% Group1 | total_KB$SYMBOL2 %in% Group2 ~ total_KB$SYMBOL2,
.default = ""
)
# Generating volcano plot
# arrange() command sorts the named points on top so they aren't buried underneath
ggplot(arrange(total_KB, CatNames), aes(x = `log2(FC)`, y = -log10(`P value`))) +
geom_point(aes(shape = CatShapes, color = CatShapes)) +
geom_vline(xintercept = c(total_FC_threshold, -total_FC_threshold), alpha = 0.5, linetype = "dashed") +
geom_hline(yintercept = -log10(total_p_threshold), alpha = 0.5, linetype = "dashed") +
geom_text_repel(aes(label = CatNames), size = rel(3), show.legend = FALSE, max.overlaps = Inf) +
guides(alpha = "none", shape = "none") +
labs(
x = "log2(Fold Change)", y = "-log10(P value)",
title = "Figure 1.1b: Volcano plot with groups"
) +
theme(legend.position = "bottom") +
scale_fill_viridis_d("Category",
aesthetics = "color", option = "magma",
begin = 0.2, end = 0.9,
labels = c("Group 2", "Group 1", "None")
)

Pathway
enrichment
This section performs a pathway analysis on the significant proteins
alone to reduce computational load when calling enrichGO
and enrichKEGG. These variables can be easily adjusted to
analyze all proteins together if desired.
In our source dataset, there were no downregulated KEGG pathways.
This causes error messages, so the “Total KEGG down” plot has been
removed. The call to labels below illustrates a difficulty
with plotting long ontology terms, but the label_format
parameter is an easy way to solve this. Outside of
enrichplot::dotplot, calling label_wrap from
labels and passing a character count to wrap at is a good
solution. We also show a way to manually replace long names later in
this section.
# Separate total_significant subset into upregulated vs downregulated
total_significant_up <- filter(total_significant, `log2(FC)` > 0)
total_significant_down <- filter(total_significant, `log2(FC)` < 0)
# Enriching for *total* GO terms - optional, can be used to visualize overall results
total_GO_up <- enrichGO(total_significant_up$ENTREZID, "org.Hs.eg.db")
total_GO_down <- enrichGO(total_significant_down$ENTREZID, "org.Hs.eg.db")
# Enrich for KEGG pathways
total_KEGG_up <- enrichKEGG(total_significant_up$ENTREZID)
## Reading KEGG annotation online: "https://rest.kegg.jp/link/hsa/pathway"...
## Reading KEGG annotation online: "https://rest.kegg.jp/list/pathway/hsa"...
total_KEGG_down <- enrichKEGG(total_significant_down$ENTREZID)
# dotplot() can be used for an overview of the relationships between key terms
dotplot(total_KEGG_up,
showCategory = 20, font.size = rel(1),
title = "Figure 1.2a: Total KEGG up"
) +
theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

dotplot(total_GO_up,
showCategory = 20, font.size = rel(1),
title = "Figure 1.2b: Total GO up (unwrapped)"
) +
theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

dotplot(total_GO_up,
showCategory = 20, font.size = rel(1),
title = "Figure 1.2c: Total GO up (wrapped)", label_format = 60
) +
theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

dotplot(total_GO_down,
showCategory = 20, font.size = rel(1),
title = "Figure 1.2d: Total GO down"
) +
theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

For each subset of data, the top 10 terms are found in each GO aspect
(molecular function (MF), biological process (BP), cellular component
(CC)). To include more terms, adjust the n = 10 parameter
in the calls to slice_min().
In this section, we also reword some very long GO terms to fit them
in an appropriate-size plot. This list will need to be adjusted based on
the enriched terms in an individual dataset.
# Separating the GO terms by ontology aspect
total_GO_up_MF <- enrichGO(total_significant_up$ENTREZID,
"org.Hs.eg.db",
ont = "MF"
)
total_GO_up_BP <- enrichGO(total_significant_up$ENTREZID,
"org.Hs.eg.db",
ont = "BP"
)
total_GO_up_CC <- enrichGO(total_significant_up$ENTREZID,
"org.Hs.eg.db",
ont = "CC"
)
# Taking the top 10 enriched GO terms in each category by P value
total_GO_up_slice_MF <- slice_min(
filter(total_GO_up_MF@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
total_GO_up_slice_BP <- slice_min(
filter(total_GO_up_BP@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
total_GO_up_slice_CC <- slice_min(
filter(total_GO_up_CC@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
# Combining top 10 terms in each aspect
total_GO_up_slice_ALL <- bind_rows(
"MF" = total_GO_up_slice_MF,
"BP" = total_GO_up_slice_BP,
"CC" = total_GO_up_slice_CC,
.id = "Ontology"
)
total_GO_up_slice_ALL$GR <- parse_ratio(total_GO_up_slice_ALL$GeneRatio)
# Added in to shorten names when plotted - this can be removed or adjusted
total_GO_up_slice_ALL["Description"][total_GO_up_slice_ALL["Description"] == "hydrolase activity, acting on carbon-nitrogen (but not peptide) bonds"] <- "hydrolase, C-N non-peptide bonds"
total_GO_up_slice_ALL["Description"][total_GO_up_slice_ALL["Description"] == "hydrolase activity, acting on carbon-nitrogen (but not peptide) bonds, in cyclic amidines"] <- "hydrolase, C-N non-peptide bonds in cyclic amidines"
total_GO_up_slice_ALL["Description"][total_GO_up_slice_ALL["Description"] == "antigen processing and presentation of endogenous peptide antigen via MHC class I"] <- "endogenous antigen processing and presentation (MHC class I)"
total_GO_up_slice_ALL["Description"][total_GO_up_slice_ALL["Description"] == "antigen processing and presentation of peptide antigen via MHC class I"] <- "peptide antigen processing and presentation (MHC class I)"
total_GO_up_slice_ALL["Description"][total_GO_up_slice_ALL["Description"] == "antigen processing and presentation of endogenous peptide antigen"] <- "endogenous antigen processing and presentation"
total_GO_up_slice_ALL["Description"][total_GO_up_slice_ALL["Description"] == "exonuclease activity, active with either ribo- or deoxyribonucleic acids and producing 5'-phosphomonoesters"] <- "5'-exonucleases"
# Reordering the top terms by gene ratio
total_GO_up_slice_ALL$Description <- fct_reorder(
total_GO_up_slice_ALL$Description, total_GO_up_slice_ALL$GR
)
# Repeat for KEGG
# Taking the top 10 enriched terms by P value
total_KEGG_up_slice <- slice_min(filter(total_KEGG_up@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
# Reordering the top terms by gene ratio
total_KEGG_up_slice$GR <- parse_ratio(total_KEGG_up_slice$GeneRatio)
total_KEGG_up_slice$Description <- fct_reorder(
total_KEGG_up_slice$Description, total_KEGG_up_slice$GR
)
total_KEGG_up_slice$Ontology <- "KEGG"
# Combining GO and KEGG for upregulated proteins
total_up_bound <- bind_rows(
total_GO_up_slice_ALL, total_KEGG_up_slice
)
total_up_bound$Description <- fct_reorder(
total_up_bound$Description, total_up_bound$GR
)
# Manually ordering factors to display all GO aspects together
total_up_bound$Ontology <- factor(total_up_bound$Ontology,
levels = c("BP", "CC", "MF", "KEGG")
)
# Graphing all 4 categories together
ggplot(
total_up_bound,
# Gene ratio on X axis, verbose pathway description on Y axis
# Coloring by adjusted P value and size by the count of genes in a pathway
aes(x = GR, y = Description, color = p.adjust, size = Count)
) +
scale_y_discrete(labels = label_wrap(70)) +
geom_count() +
labs(
x = "Gene ratio", y = "Pathway",
title = "Figure 1.2e: Upregulated pathways"
) +
scale_fill_viridis(
aesthetics = "color", option = "magma",
name = "Adjusted P", begin = 0, end = 0.9
) +
scale_shape_manual(values = c(25, 24)) +
facet_grid(Ontology ~ .,
drop = TRUE, space = "free_y", scales = "free_y"
) +
theme(
strip.text = element_text(size = 10),
panel.spacing = unit(0.6, "lines"),
axis.text.y = element_text(size = rel(1.1)),
panel.background = element_rect(fill = "white"),
panel.border = element_rect(fill = NA, color = "light gray"),
panel.grid = element_line(color = "gray90")
)

# Same process as the previous chunk
# Separating the GO terms by ontology aspect
total_GO_down_MF <- enrichGO(total_significant_down$ENTREZID,
"org.Hs.eg.db",
ont = "MF"
)
total_GO_down_BP <- enrichGO(total_significant_down$ENTREZID,
"org.Hs.eg.db",
ont = "BP"
)
total_GO_down_CC <- enrichGO(total_significant_down$ENTREZID,
"org.Hs.eg.db",
ont = "CC"
)
# Taking the top 10 enriched terms in each category by P value
total_GO_down_slice_MF <- slice_min(
filter(total_GO_down_MF@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
total_GO_down_slice_BP <- slice_min(
filter(total_GO_down_BP@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
total_GO_down_slice_CC <- slice_min(
filter(total_GO_down_CC@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
# Combining top 10 terms in each aspect
total_down_bound <- bind_rows(
"MF" = total_GO_down_slice_MF,
"BP" = total_GO_down_slice_BP,
"CC" = total_GO_down_slice_CC,
.id = "Ontology"
)
total_down_bound$GR <- parse_ratio(total_down_bound$GeneRatio)
# Reordering the top terms by gene ratio
total_down_bound$Description <- fct_reorder(
total_down_bound$Description, total_down_bound$GR
)
total_down_bound$Ontology <- factor(total_down_bound$Ontology,
levels = c("BP", "CC", "MF", "KEGG")
)
# KEGG enrichment did not show anything so it caused an error in our data and has been removed
# If desired, KEGG enrichment can be performed here as in the previous chunk
ggplot(
total_down_bound,
aes(x = GR, y = Description, color = p.adjust, size = Count)
) +
geom_count() +
labs(
x = "Gene ratio", y = "Pathway",
title = "Figure 1.2f: Downregulated pathways"
) +
scale_fill_viridis(
aesthetics = "color", option = "magma",
name = "Adjusted P", begin = 0, end = 0.9
) +
scale_shape_manual(values = c(25, 24)) +
facet_grid(Ontology ~ .,
drop = TRUE, space = "free_y",
scales = "free_y"
) +
theme(
strip.text = element_text(size = 10),
panel.spacing = unit(0.6, "lines"),
axis.text.y = element_text(size = rel(1.1)),
panel.background = element_rect(fill = "white"),
panel.border = element_rect(fill = NA, color = "light gray"),
panel.grid = element_line(color = "gray90")
)

Figure 1.2c clusters terms by function, which shows enriched
domains.
# Plotting the enrichment plot for one aspect of upregulated pathways
# Enrich first with pairwise_term_sim, then generate the plot itself
total_GO_up_MF <- pairwise_termsim(total_GO_up_MF)
emapplot(total_GO_up_MF, layout = "nicely", node_label = "none") +
ggtitle("Figure 1.2g: Upregulated enrichment plot (MF)") +
geom_text_repel(aes(label = str_wrap(name, width = 60))) +
theme(
plot.title.position = "plot", plot.title = element_text(hjust = 0.5),
plot.margin = margin(1, 1, 1, 1)
)

Phosphoproteome data
processing
This section is written to interpret phosphoproteome data. Here, we
do not account for particular phosphorylation sites or their functions.
The original data is from Choudhary et al.
(2020). Below is a reprocessing of this data to align with the
total and histone proteome.
# Read in phosphoproteome data, assign symbol to row names
# Already formatted so there's no need to map with reflist
phospho_raw <- read_xlsx("~/Documents/R/PXD_raw.xlsx")
phospho_raw <- phospho_raw %>%
mutate("SYMBOL2" = `SYMBOL`) %>%
column_to_rownames(var = "SYMBOL") %>%
drop_na(SYMBOL2, `log2(FC)`, `P value`) %>%
distinct(SYMBOL2, .keep_all = TRUE) %>%
mutate(`...6` = NULL)
# Set threshold values for proteome analysis
phospho_p_threshold <- 0.05
phospho_FC_threshold <- 0.5
phospho_significant <- filter(phospho_raw, `P value` < phospho_p_threshold)
Volcano
# Repeating the processes from before
# If your groups are too large, they will not display as all labeled at once
Group1_phospho <- c(
"HJURP", "CHAF1B", "RBL1", "EYA3", "BRCA1",
"NCOA6", "PALB2", "BARD1", "REPS2", "ZNF511", "BRWD1",
"L3MBTL3", "WDR76", "CHAF1A", "PIAS2", "RUNX1", "RB1", "JDP2",
"EP400", "MRGBP", "EMSY", "RYBP", "HIRIP3", "TOPBP1",
"MGA", "PRDM8", "EZH2", "HDAC5", "DMAP1", "NPAT", "SAP130",
"SAMD1", "ZMYND8", "ASH1L", "ARID1B", "HDAC4", "BRD9", "NCOR1",
"MCM3AP", "UTP3", "ZZEF1", "DNTTIP1", "PRKD1", "MTA1", "SATB2",
"KANSL3", "REST", "SIRT6", "PHC2", "TOPORS", "JMJD1C", "BRD2",
"ATF7IP", "SP1", "KANSL1", "BMI1", "COMMD3-BMI1", "KMT2C", "ASXL2",
"TP53BP1", "YY1AP1", "WAC", "BAP1", "L3MBTL2", "ZNF518A", "MBD3",
"OBI1", "DOT1L", "KDM5C", "RING1", "USP16", "CNOT3", "ATXN1L", "RCOR3",
"RCOR2", "SCML2", "KMT2D", "ARID1A", "GATAD2B", "ATM", "GTF3C4", "SP140",
"CHD8", "BPTF", "MECP2", "AHCTF1", "KDM5A", "MYSM1", "ATF7IP2", "NIPBL",
"LRWD1", "ASH2L", "MARK3", "RRP1B", "EP300", "EHMT2", "TNRC18", "MDC1",
"RAI1", "CIR1", "FOXO3", "ASXL1", "UBE2O", "KDM2B", "DPF2", "BRD4",
"NACC1", "CHD4", "TRIM28", "RUVBL1", "ACTL6B", "DDB1", "ACTL6A",
"STK4", "CARM1", "SUMO3", "ENY2", "CUL5", "PHF5A", "SIRT2", "PKN1",
"MAPKAPK2", "MAGEA1", "ZNF518B", "SMCHD1", "DLD", "MEN1", "HDAC6",
"DTX3L", "PSMA8", "H1-6", "PML", "NEK6", "PHF20L1", "RBBP6"
)
Group2_phospho <- c(
"FANCI", "CDK14", "PRDM1", "ICAM1", "CDK1", "RUNX3", "NOTCH3", "CASP8",
"BST2", "CTSL", "MX2", "CASP1", "STAT2", "MX1"
)
phospho_raw$DiffExp <- case_when(
abs(phospho_raw$`log2(FC)`) >= phospho_FC_threshold &
phospho_raw$`P value` > phospho_p_threshold ~ "FC only",
abs(phospho_raw$`log2(FC)`) < phospho_FC_threshold &
phospho_raw$`P value` <= phospho_p_threshold ~ "P val. only",
abs(phospho_raw$`log2(FC)`) >= phospho_FC_threshold &
phospho_raw$`P value` <= phospho_p_threshold ~ "FC and P val.",
.default = "NS"
)
phospho_raw$DiffExp <- factor(phospho_raw$DiffExp,
levels = c("FC and P val.", "P val. only", "FC only", "NS")
)
# Adding in an alpha value due to point spread and higher hit count
phospho_raw$DEAlpha <- 0.3
phospho_raw$DEAlpha[abs(phospho_raw$`log2(FC)`) >= phospho_FC_threshold &
phospho_raw$`P value` <= phospho_p_threshold] <- 0.8
# Picking a much smaller subset of points in Group 1 to label just for ease of viewing
Group1_phospho_sub <- c(
"ASH2L", "ASXL1", "ASXL2", "BAP1", "BMI1", "CHD4", "EZH2", "DMAP1",
"EP400", "GATAD2B", "KANSL1", "KANSL3", "KMT2C", "KMT2D", "MBD3",
"MEN1", "MGA", "MRGBP", "MTA1", "NCOA6", "PHC2", "PHF20L1", "REST",
"RING1", "RUVBL1", "SAP130", "SCML2", "SIRT3", "SIRT6", "EP300"
)
phospho_raw$labels <- if_else(
abs(phospho_raw$`log2(FC)`) >= phospho_FC_threshold &
phospho_raw$`P value` <= phospho_p_threshold, phospho_raw$SYMBOL2, ""
)
ggplot(phospho_raw, aes(x = `log2(FC)`, y = -log10(`P value`))) +
geom_point(aes(color = DiffExp, alpha = DEAlpha)) +
geom_vline(
xintercept = c(phospho_FC_threshold, -phospho_FC_threshold),
alpha = 0.5, linetype = "dashed"
) +
geom_hline(
yintercept = -log10(phospho_p_threshold),
alpha = 0.5, linetype = "dashed"
) +
geom_text_repel(aes(label = labels),
show.legend = FALSE, max.overlaps = 20,
size = rel(3)
) +
guides(alpha = "none") +
labs(
x = "log2(Fold Change)", y = "-log10(P value)",
title = "Figure 2.1a: Volcano plot",
color = "Category"
) +
theme(legend.position = "bottom") +
scale_fill_viridis_d(
aesthetics = "color", option = "magma",
begin = 0.2, end = 0.9
)
## Warning: ggrepel: 1275 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

Pathway
enrichment
# Separate phospho_significant subset into upregulated vs downregulated
phospho_significant_up <- filter(phospho_significant, `log2(FC)` > 0)
phospho_significant_down <- filter(phospho_significant, `log2(FC)` < 0)
# Enriching for *total* GO terms - optional, can be used to visualize overall results
phospho_GO_up <- enrichGO(phospho_significant_up$ENTREZID, "org.Hs.eg.db")
phospho_GO_down <- enrichGO(phospho_significant_down$ENTREZID, "org.Hs.eg.db")
# Enrich for KEGG pathways
phospho_KEGG_up <- enrichKEGG(phospho_significant_up$ENTREZID)
phospho_KEGG_down <- enrichKEGG(phospho_significant_down$ENTREZID)
# dotplot() can be used for an overview of the relationships between key terms
dotplot(phospho_KEGG_up,
showCategory = 20, font.size = rel(1),
title = "Figure 2.2a: Phospho KEGG up", label_format = 50
) +
theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

dotplot(phospho_KEGG_down,
showCategory = 20, font.size = rel(1),
title = "Figure 2.2b: Phospho KEGG down", label_format = 50
) +
theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

dotplot(phospho_GO_up,
showCategory = 20, font.size = rel(1),
title = "Figure 2.2c: Phospho GO up", label_format = 70
) +
theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

dotplot(phospho_GO_down,
showCategory = 20, font.size = rel(1),
title = "Figure 2.2d: Phospho GO down", label_format = 50
) +
theme(plot.title.position = "plot", plot.title = element_text(hjust = 0.5))

# Separating the GO terms by ontology aspect
phospho_GO_up_MF <- enrichGO(phospho_significant_up$ENTREZID,
"org.Hs.eg.db",
ont = "MF"
)
phospho_GO_up_BP <- enrichGO(phospho_significant_up$ENTREZID,
"org.Hs.eg.db",
ont = "BP"
)
phospho_GO_up_CC <- enrichGO(phospho_significant_up$ENTREZID,
"org.Hs.eg.db",
ont = "CC"
)
# Taking the top 10 enriched terms in each category by P value
phospho_GO_up_slice_MF <- slice_min(
filter(phospho_GO_up_MF@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
phospho_GO_up_slice_BP <- slice_min(
filter(phospho_GO_up_BP@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
phospho_GO_up_slice_CC <- slice_min(
filter(phospho_GO_up_CC@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
# Combining top 10 terms in each aspect
phospho_GO_up_slice_ALL <- bind_rows(
"MF" = phospho_GO_up_slice_MF,
"BP" = phospho_GO_up_slice_BP,
"CC" = phospho_GO_up_slice_CC,
.id = "Ontology"
)
phospho_GO_up_slice_ALL$GR <- parse_ratio(phospho_GO_up_slice_ALL$GeneRatio)
# Added in to shorten names when plotted - this can be removed or adjusted
phospho_GO_up_slice_ALL["Description"][phospho_GO_up_slice_ALL["Description"] == "oxidoreductase activity, acting on the aldehyde or oxo group of donors, NAD or NADP as acceptor"] <- "oxidoreductase, aldehyde/oxo donor, NAD/NADP acceptor"
# Reordering the top terms by gene ratio
phospho_GO_up_slice_ALL$Description <- fct_reorder(
phospho_GO_up_slice_ALL$Description, phospho_GO_up_slice_ALL$GR
)
# Repeat for KEGG
# Taking the top 10 enriched terms by P value
phospho_KEGG_up_slice <- slice_min(
filter(phospho_KEGG_up@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
# Reordering the top terms by gene ratio
phospho_KEGG_up_slice$GR <- parse_ratio(phospho_KEGG_up_slice$GeneRatio)
phospho_KEGG_up_slice$Description <- fct_reorder(
phospho_KEGG_up_slice$Description,
phospho_KEGG_up_slice$GR
)
phospho_KEGG_up_slice$Ontology <- "KEGG"
# Combining GO and KEGG for upregulated proteins
phospho_up_bound <- bind_rows(
phospho_GO_up_slice_ALL, phospho_KEGG_up_slice
)
phospho_up_bound$Description <- fct_reorder(
phospho_up_bound$Description, phospho_up_bound$GR
)
# Manually ordering factors to display all GO aspects together
phospho_up_bound$Ontology <- factor(phospho_up_bound$Ontology,
levels = c("BP", "CC", "MF", "KEGG")
)
# Graphing all 4 categories together
ggplot(
phospho_up_bound,
# Gene ratio on X axis, verbose pathway description on Y axis
# Coloring by adjusted P value and size by the count of genes in a pathway
aes(x = GR, y = Description, color = p.adjust, size = Count)
) +
scale_y_discrete(labels = label_wrap(80)) +
geom_count() +
labs(
x = "Gene ratio", y = "Pathway",
title = "Figure 2.2e: Upregulated pathways"
) +
scale_fill_viridis(
aesthetics = "color", option = "magma",
name = "Adjusted P", begin = 0, end = 0.9
) +
scale_shape_manual(values = c(25, 24)) +
facet_grid(Ontology ~ .,
drop = TRUE, space = "free_y",
scales = "free_y"
) +
theme(
strip.text = element_text(size = 10),
panel.spacing = unit(0.6, "lines"),
axis.text.y = element_text(size = rel(1.1)),
panel.background = element_rect(fill = "white"),
panel.border = element_rect(fill = NA, color = "light gray"),
panel.grid = element_line(color = "gray90")
)

# Same process as the previous chunks
# Separating the GO terms by ontology aspect
phospho_GO_down_MF <- enrichGO(phospho_significant_down$ENTREZID,
"org.Hs.eg.db",
ont = "MF"
)
phospho_GO_down_BP <- enrichGO(phospho_significant_down$ENTREZID,
"org.Hs.eg.db",
ont = "BP"
)
phospho_GO_down_CC <- enrichGO(phospho_significant_down$ENTREZID,
"org.Hs.eg.db",
ont = "CC"
)
# Taking the top 10 enriched terms in each category by P value
phospho_GO_down_slice_MF <- slice_min(
filter(phospho_GO_down_MF@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
phospho_GO_down_slice_BP <- slice_min(
filter(phospho_GO_down_BP@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
phospho_GO_down_slice_CC <- slice_min(
filter(phospho_GO_down_CC@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
# Combining top 10 terms in each aspect
phospho_down_bound <- bind_rows(
"MF" = phospho_GO_down_slice_MF,
"BP" = phospho_GO_down_slice_BP,
"CC" = phospho_GO_down_slice_CC,
.id = "Ontology"
)
# Repeat for KEGG
# Taking the top 10 enriched terms by P value
phospho_KEGG_down_slice <- slice_min(
filter(phospho_KEGG_down@result, p.adjust < 0.05),
order_by = `p.adjust`, n = 10
)
# Reordering the top terms by gene ratio
phospho_KEGG_down_slice$GR <- parse_ratio(phospho_KEGG_down_slice$GeneRatio)
phospho_KEGG_down_slice$Description <- fct_reorder(
phospho_KEGG_down_slice$Description,
phospho_KEGG_down_slice$GR
)
phospho_KEGG_down_slice$Ontology <- "KEGG"
# Combining GO and KEGG for downregulated proteins
phospho_down_bound <- bind_rows(
phospho_down_bound, phospho_KEGG_down_slice
)
phospho_down_bound$GR <- parse_ratio(phospho_down_bound$GeneRatio)
phospho_down_bound$Description <- fct_reorder(
phospho_down_bound$Description, phospho_down_bound$GR
)
# Manually ordering factors to display all GO aspects together
phospho_down_bound$Ontology <- factor(phospho_down_bound$Ontology,
levels = c("BP", "CC", "MF", "KEGG")
)
# Graphing all 4 categories together
ggplot(
phospho_down_bound,
# Gene ratio on X axis, verbose pathway description on Y axis
# Coloring by adjusted P value and size by the count of genes in a pathway
aes(x = GR, y = Description, color = p.adjust, size = Count)
) +
scale_y_discrete(labels = label_wrap(70)) +
geom_count() +
labs(
x = "Gene ratio", y = "Pathway",
title = "Figure 2.2f: Downregulated pathways"
) +
scale_fill_viridis(
aesthetics = "color", option = "magma",
name = "Adjusted P", begin = 0, end = 0.9
) +
scale_shape_manual(values = c(25, 24)) +
facet_grid(Ontology ~ .,
drop = TRUE, space = "free_y",
scales = "free_y"
) +
theme(
strip.text = element_text(size = 10),
panel.spacing = unit(0.6, "lines"),
axis.text.y = element_text(size = rel(1.1)),
panel.background = element_rect(fill = "white"),
panel.border = element_rect(fill = NA, color = "light gray"),
panel.grid = element_line(color = "gray90")
)

phospho_GO_up_MF <- pairwise_termsim(phospho_GO_up_MF)
emapplot(phospho_GO_up_MF, layout = "nicely", node_label = "none") +
geom_text_repel(aes(label = str_wrap(name, width = 60))) +
ggtitle("Figure 2.2g: Upregulated enrichment plot (phospho, MF)") +
theme(
plot.title.position = "plot", plot.title = element_text(hjust = 0.5),
plot.margin = margin(1, 1, 1, 1)
)

phospho_GO_down_MF <- pairwise_termsim(phospho_GO_down_MF)
emapplot(phospho_GO_down_MF, layout = "nicely", node_label = "none") +
geom_text_repel(aes(label = str_wrap(name, width = 60))) +
ggtitle("Figure 2.2h: Downregulated enrichment plot (phospho, MF)") +
theme(
plot.title.position = "plot", plot.title = element_text(hjust = 0.5),
plot.margin = margin(1, 1, 1, 1)
)

Histone processing
This section processes data output from EpiProfile. We choose to
assess both tail fragments and individual summed PTMs to indicate
co-occurrence, which may signify activity of certain epigenetic actors.
Note that the exported .xls files will have to be opened
manually one time, then saved again as .xls(x) format due
to the output format. Into the range argument, pass the
cell location of the first peptide name (“H3_3_8 unmod” in our data) to
set the top left corner, then (NA, [target col]) as the second location
to set the bottom right corner. This section assumes the data is
separating two conditions, infected and control.
histone_ratios_path <- "~/Documents/R/histone_ratios/histone_ratios.xls"
histone_single_path <- "~/Documents/R/histone_ratios/histone_ratios_single_PTMs.xls"
# The columns will have to be manually parsed - include the names (first column) and ratios for all samples
histone_frag_raw <- drop_na(read_excel(path = histone_ratios_path, range = cell_limits(c(4, 1), c(NA, 11)), col_names = c("PTM", "C1", "I1", "C2", "I2", "C3", "I3", "C4", "I4", "C5", "I5")))
histone_single_raw <- read_excel(path = histone_single_path, col_names = c("PTM", "C1", "I1", "C2", "I2", "C3", "I3", "C4", "I4", "C5", "I5"), range = cell_limits(c(2, 1), c(NA, 11)))
# Set threshold values for proteome analysis
histone_p_threshold <- 0.05
histone_FC_threshold <- 0.5
Fragments
Name cleanup is not strictly necessary but makes labels easier to
read. There may also be some long amino acid strings such as “H1 1-35
H12.SETAPAAPAAAPPAEKAPVKKKAAKKAGGTPR” that indicate a unique isoform
(H1.2 in this case). These can also be modified by calling
str_replace_all as desired.
# Cleaning up names
ratios_frag <- histone_frag_raw
ratios_frag$PTM <- ratios_frag$PTM %>%
str_replace_all("H3_3_8", "H3 3-8") %>%
str_replace_all("H3_9_17", "H3 9-17") %>%
str_replace_all("H3_18_26", "H3 18-26") %>%
str_replace_all("H3_27_40", "H3 27-40") %>%
str_replace_all("H33_27_40", "H3.3 27-40") %>%
str_replace_all("H3_41_49", "H3 41-49") %>%
str_replace_all("H3_54_63", "H3 54-63") %>%
str_replace_all("H3_73_83", "H3 73-83") %>%
str_replace_all("H3_117_128", "H3 117-128") %>%
str_replace_all("H4_4_17", "H4 4-17") %>%
str_replace_all("H4_20_23", "H4 20-23") %>%
str_replace_all("H4_24_35", "H4 24-35") %>%
str_replace_all("H4_40_45", "H4 40-45") %>%
str_replace_all("H4_79_92", "H4 79-92") %>%
str_replace_all("H1_1_35", "H1 1-35") %>%
str_replace_all("H1_54_81", "H1 54-71") %>%
str_replace_all("H2A1_36_42", "H2A1 36-42") %>%
str_replace_all("H2A3_36_42", "H2A3 36-42") %>%
str_replace_all("H2AX_36_42", "H2AX 36-42") %>%
str_replace_all("H2A1_4_11", "H2A1 4-11") %>%
str_replace_all("H2AJ_4_11", "H2AJ 4-11") %>%
str_replace_all("H2AX_4_11", "H2AX 4-11") %>%
str_replace_all("H2A1_1_11", "H2A1 1-11") %>%
str_replace_all("H2AV_1_19", "H2AV 1-19") %>%
str_replace_all("H2AZ_1_19", "H2AZ 1-19") %>%
str_replace_all("H2A1_12_17", "H2A1 12-17") %>%
str_replace_all("H2A3_12_17", "H2A3 12-17") %>%
str_replace_all("H2A1_72_77", "H2A1 72-77") %>%
str_replace_all("H2A_82_88", "H2A 82-88") %>%
str_replace_all("H2A_1_88", "H2A 1-88") %>%
str_replace_all("H2B_1_29", "H2B 1-29")
# Group two conditions together
ratios_frag <- ratios_frag %>%
relocate(contains("I"), .after = last_col()) %>%
relocate("PTM")
# Calculate infected/control fold change and P value
ratios_frag <- ratios_frag %>%
rowwise() %>%
mutate(
c_mean = mean(c_across("C1":"C5")),
i_mean = mean(c_across("I1":"I5")),
FC = c_across(i_mean) / c_across(c_mean),
"log2(FC)" = log2(FC),
is_constant = all(c_across("C1":"C5") == c_across("I1":"I5"))
)
ratios_frag <- column_to_rownames(ratios_frag, var = "PTM")
ratios_frag$PTM <- rownames(ratios_frag)
ratios_frag$"P value" <- NA
ratios_p <- ratios_frag %>%
rowwise() %>%
filter(is_constant == FALSE) %>%
# Removing any proteins that have all equal values, which gives an
# error when running t.test()
mutate("P value" = t.test(c_across("C1":"C5"), c_across("I1":"I5"),
alternative = "two.sided", var.equal = TRUE
)$p.value)
ratios_frag <- full_join(ratios_frag, ratios_p)
## Joining with `by = join_by(C1, C2, C3, C4, C5, I1, I2, I3, I4, I5, c_mean,
## i_mean, FC, `log2(FC)`, is_constant, PTM, `P value`)`
ratios_frag$`log2(FC)`[ratios_frag$`log2(FC)` == "NaN"] <- NA
ratios_frag$FC[ratios_frag$FC == "NaN"] <- NA
To address infinite fold change values that appear when a protein is
not detected in one condition, we set them to have a unique shape in the
final plot and assign a fold change value equal to 1.5 times the maximum
valid value.
# Generating overall volcano plot
# Setting categories for differential expression:
# not significant, P-value only, FC only, both
ratios_frag$DiffExp <- case_when(
abs(ratios_frag$`log2(FC)`) >= histone_FC_threshold &
ratios_frag$`P value` > histone_p_threshold ~ "FC only",
abs(ratios_frag$`log2(FC)`) < histone_FC_threshold &
ratios_frag$`P value` <= histone_p_threshold ~ "P val. only",
abs(ratios_frag$`log2(FC)`) >= histone_FC_threshold &
ratios_frag$`P value` <= histone_p_threshold ~ "FC and P val.",
.default = "NS"
)
ratios_frag$DiffExp <- factor(ratios_frag$DiffExp,
levels = c("FC and P val.", "P val. only", "FC only", "NS")
)
ratios_frag$labels <- if_else(
abs(ratios_frag$`log2(FC)`) >= histone_FC_threshold &
ratios_frag$`P value` <= histone_p_threshold, ratios_frag$PTM, ""
)
ratios_frag_plot <- ratios_frag %>%
drop_na() %>%
filter(`log2(FC)` != "-Inf" & `log2(FC)` != "Inf")
ratios_frag_plot$shape <- "16"
ratios_frag_invalid <- ratios_frag %>%
drop_na() %>%
filter(`log2(FC)` == "-Inf" | `log2(FC)` == "Inf")
ratios_frag_invalid$`log2(FC)`[ratios_frag_invalid$`log2(FC)` == "-Inf"] <- min(ratios_frag_plot$`log2(FC)`) * 1.5
ratios_frag_invalid$`log2(FC)`[ratios_frag_invalid$`log2(FC)` == "Inf"] <- max(ratios_frag_plot$`log2(FC)`) * 1.5
ratios_frag_invalid$shape <- "17"
ratios_frag_plot <- full_join(ratios_frag_plot, ratios_frag_invalid)
## Joining with `by = join_by(C1, C2, C3, C4, C5, I1, I2, I3, I4, I5, c_mean,
## i_mean, FC, `log2(FC)`, is_constant, PTM, `P value`, DiffExp, labels, shape)`
ggplot(ratios_frag_plot, aes(x = `log2(FC)`, y = -log10(`P value`))) +
geom_point(aes(color = DiffExp, shape = shape)) +
expand_limits(x = c(min(ratios_frag_plot$`log2(FC)`), max(ratios_frag_plot$`log2(FC)`))) +
geom_vline(
xintercept = c(histone_FC_threshold, -histone_FC_threshold),
alpha = 0.5, linetype = "dashed"
) +
geom_hline(
yintercept = -log10(histone_p_threshold),
alpha = 0.5, linetype = "dashed"
) +
geom_text_repel(aes(label = labels),
show.legend = FALSE, max.overlaps = 20,
size = rel(3)
) +
guides(shape = "none") +
labs(
x = "log2(Fold Change)", y = "-log10(P value)",
title = "Figure 3.1a: Fragment volcano plot",
color = "Category"
) +
theme(legend.position = "bottom") +
scale_fill_viridis_d(
aesthetics = "color", option = "magma",
begin = 0.2, end = 0.9
)

Single
# Cleaning up names
ratios_single <- histone_single_raw
ratios_single$PTM <- ratios_single$PTM %>%
str_replace_all("H31", "H3.1") %>%
str_replace_all("H33", "H3.3")
# Group two conditions together
ratios_single <- ratios_single %>%
relocate(contains("I"), .after = last_col()) %>%
relocate("PTM")
# Calculate infected/control fold change and P value
ratios_single <- ratios_single %>%
rowwise() %>%
mutate(
c_mean = mean(c_across("C1":"C5")),
i_mean = mean(c_across("I1":"I5")),
FC = c_across(i_mean) / c_across(c_mean),
"log2(FC)" = log2(FC),
"P value" = t.test(c_across("C1":"C5"), c_across("I1":"I5"),
alternative = "two.sided", var.equal = TRUE
)$p.value
)
ratios_single <- column_to_rownames(ratios_single, var = "PTM")
ratios_single$PTM <- rownames(ratios_single)
ratios_single <- ratios_single %>%
rowwise() %>%
mutate(
c_mean = mean(c_across("C1":"C5")),
i_mean = mean(c_across("I1":"I5")),
FC = c_across(i_mean) / c_across(c_mean),
"log2(FC)" = log2(FC),
is_constant = all(c_across("C1":"C5") == c_across("I1":"I5"))
)
ratios_single <- column_to_rownames(ratios_single, var = "PTM")
ratios_single$PTM <- rownames(ratios_single)
ratios_single$"P value" <- NA
single_p <- ratios_single %>%
rowwise() %>%
filter(is_constant == FALSE) %>%
# Removing any proteins that have all equal values, which gives an
# error when running t.test()
mutate("P value" = t.test(c_across("C1":"C5"), c_across("I1":"I5"),
alternative = "two.sided", var.equal = TRUE
)$p.value)
ratios_single <- full_join(ratios_single, single_p)
## Joining with `by = join_by(C1, C2, C3, C4, C5, I1, I2, I3, I4, I5, c_mean,
## i_mean, FC, `log2(FC)`, `P value`, is_constant, PTM)`
ratios_single$`log2(FC)`[ratios_single$`log2(FC)` == "NaN"] <- NA
ratios_single$FC[ratios_single$FC == "NaN"] <- NA
# Generating overall volcano plot - same as previous
# Setting categories for differential expression:
# not significant, P-value only, FC only, both
ratios_single$DiffExp <- case_when(
abs(ratios_single$`log2(FC)`) >= histone_FC_threshold &
ratios_single$`P value` > histone_p_threshold ~ "FC only",
abs(ratios_single$`log2(FC)`) < histone_FC_threshold &
ratios_single$`P value` <= histone_p_threshold ~ "P val. only",
abs(ratios_single$`log2(FC)`) >= histone_FC_threshold &
ratios_single$`P value` <= histone_p_threshold ~ "FC and P val.",
.default = "NS"
)
ratios_single$DiffExp <- factor(ratios_single$DiffExp,
levels = c("FC and P val.", "P val. only", "FC only", "NS")
)
# Setting labels
ratios_single$labels <- if_else(
abs(ratios_single$`log2(FC)`) >= histone_FC_threshold &
ratios_single$`P value` <= histone_p_threshold, ratios_single$PTM, ""
)
ratios_single_plot <- ratios_single %>%
drop_na() %>%
filter(`log2(FC)` != "-Inf" & `log2(FC)` != "Inf")
ratios_single_plot$shape <- "16"
ratios_single_invalid <- ratios_single %>%
drop_na() %>%
filter(`log2(FC)` == "-Inf" | `log2(FC)` == "Inf")
ratios_single_invalid$`log2(FC)`[ratios_single_invalid$`log2(FC)` == "-Inf"] <- min(ratios_single_plot$`log2(FC)`) * 1.5
ratios_single_invalid$`log2(FC)`[ratios_single_invalid$`log2(FC)` == "Inf"] <- max(ratios_single_plot$`log2(FC)`) * 1.5
ratios_single_invalid$shape <- "17"
ratios_single_plot <- full_join(ratios_single_plot, ratios_single_invalid)
## Joining with `by = join_by(C1, C2, C3, C4, C5, I1, I2, I3, I4, I5, c_mean,
## i_mean, FC, `log2(FC)`, `P value`, is_constant, PTM, DiffExp, labels, shape)`
ggplot(ratios_single_plot, aes(x = `log2(FC)`, y = -log10(`P value`))) +
geom_point(aes(color = DiffExp, shape = shape)) +
expand_limits(x = c(min(ratios_single_plot$`log2(FC)`), max(ratios_single_plot$`log2(FC)`))) +
geom_vline(
xintercept = c(histone_FC_threshold, -histone_FC_threshold),
alpha = 0.5, linetype = "dashed"
) +
geom_hline(
yintercept = -log10(histone_p_threshold),
alpha = 0.5, linetype = "dashed"
) +
geom_text_repel(aes(label = labels),
show.legend = FALSE, max.overlaps = 20,
size = rel(3)
) +
guides(shape = "none") +
labs(
x = "log2(Fold Change)", y = "-log10(P value)",
title = "Figure 3.1b: Net histone volcano plot",
color = "Category"
) +
theme(legend.position = "bottom") +
scale_fill_viridis_d(
aesthetics = "color", option = "magma",
begin = 0.2, end = 0.9
)

Differential
expression
Here we begin a comparative analysis of the two larger datasets
(total proteome and phosphoproteome).
# Venn diagram to quickly look at proteome/phosphoproteome overlap
venn_lists <- list_to_data_frame(list("Set 1" = total_KB$SYMBOL2, "Set 2" = phospho_raw$SYMBOL2))
ggvenn(venn_lists, set_name_size = 5) +
ggtitle("Figure 4a: Set overlap") +
theme(plot.margin = margin(1, 1, 1, 2))

Total proteome
infected vs control
Section 4.1 directly compares the total proteome to itself. Here, we
examine the sets of proteins identified exclusively in infected cells
(identified by columns with “JMI”), control cells, or both. Detection
inputs for these columns can be “High”, “Peak Found”, or “n/a”. To
identify a protein found only in one condition, we check that at least
one replicate from that condition is not “n/a”. We then check that,
simultaneously, all replicates from the other condition are equal to
“n/a”, indicating the protein was not found. If both requirements are
met, a string is mapped to that protein indicating its presence. If a
protein has at least one replicate not equal to “n/a” in both
experimental conditions, “All” is assigned, indicating overlap.
# Identify proteins expressed in both conditions, only control, or only infected.
total_diff_valid <- total_KB %>% mutate(`Differential?` = case_when(
# Control ONLY
# BCG empty, at least 1 ctrl not empty
(((`Found in Sample: F1: Sample, Control` != "n/a") |
(`Found in Sample: F2: Sample, Control` != "n/a") |
(`Found in Sample: F3: Sample, Control` != "n/a")) &
((`Found in Sample: F4: Sample, JMI` == "n/a") &
(`Found in Sample: F5: Sample, JMI` == "n/a") &
(`Found in Sample: F5: Sample, JMI` == "n/a") &
(`Found in Sample: F6: Sample, JMI` == "n/a") &
(`Found in Sample: F7: Sample, JMI` == "n/a") &
(`Found in Sample: F8: Sample, JMI` == "n/a"))) ~ "Control only",
# Infected ONLY
# Control empty, at least one BCG not empty
(((`Found in Sample: F1: Sample, Control` == "n/a") &
(`Found in Sample: F2: Sample, Control` == "n/a") &
(`Found in Sample: F3: Sample, Control` == "n/a")) &
((`Found in Sample: F4: Sample, JMI` != "n/a") |
(`Found in Sample: F5: Sample, JMI` != "n/a") |
(`Found in Sample: F5: Sample, JMI` != "n/a") |
(`Found in Sample: F6: Sample, JMI` != "n/a") |
(`Found in Sample: F7: Sample, JMI` != "n/a") |
(`Found in Sample: F8: Sample, JMI` != "n/a"))) ~ "BCG only",
# Both
.default = "All"
))
Euler plots are sized proportionally to their groups and are more
friendly to multiple sets. To generate the Euler plots, the input needs
to be a single column containing all peptides found in that condition.
The print command provides summary statistics to ensure
that the Euler plot does not misrepresent the data.
# Pivoting to make a wider table
total_wider_valid <- pivot_wider(total_diff_valid,
names_from = `Differential?`, values_from = `SYMBOL2`
)
# Create a list of infection only peptides, removing empty rows (NA)
BCG_dist_valid <- total_wider_valid %>%
distinct(`BCG only`) %>%
drop_na()
colnames(BCG_dist_valid) <- c("Peptides")
# Create a list of control only peptides, removing empty rows (NA)
Ctrl_dist_valid <- total_wider_valid %>%
distinct(`Control only`) %>%
drop_na()
colnames(Ctrl_dist_valid) <- c("Peptides")
# Create a list of peptides present in both, removing empty rows (NA)
All_dist_valid <- total_wider_valid %>%
distinct(`All`) %>%
drop_na()
colnames(All_dist_valid) <- c("Peptides")
# Create a list of BCG + both
BCG_both_dist_valid <- add_row(BCG_dist_valid, All_dist_valid) %>% distinct()
colnames(BCG_both_dist_valid) <- c("Infected")
# Create a list of control + both
Ctrl_both_dist_valid <- add_row(Ctrl_dist_valid, All_dist_valid) %>% distinct()
colnames(Ctrl_both_dist_valid) <- c("Control")
# Generate plot
fit_total_valid <- euler(c(BCG_both_dist_valid, Ctrl_both_dist_valid))
plot(fit_total_valid,
quantities = TRUE,
fills = c("palevioletred1", "cornflowerblue", "gainsboro"),
main = "Figure 4.1a: Condition Euler plot"
)

print(fit_total_valid)
## original fitted residuals regionError
## Infected 29 29 0 0
## Control 20 20 0 0
## Infected&Control 3174 3174 0 0
##
## diagError: 0
## stress: 0
Disease enrichment
To complete the integrated analysis, we examine a gene-disease
association (GDA) summary from DisGeNET. The GDA Excel sheets can be
downloaded directly from the website after identifying the disease of
interest. Point the read_xlsx call to this file.
BC_xl <- read_xlsx("~/Documents/R/Final analysis/C2145472_disease_gda_summary.xlsx")
# Get lists of proteins that were significant AND above the FC threshold
total_FC <- filter(total_significant, abs(`log2(FC)`) > total_FC_threshold)
phospho_FC <- filter(phospho_significant, abs(`log2(FC)`) > phospho_FC_threshold)
# Generate plot
ggvenn(list(
"NMIBC" = BC_xl$Gene,
"Total proteome" = total_FC$SYMBOL2,
"Phosphoproteome" = phospho_FC$SYMBOL2
)) + ggtitle("Figure 5a: Venn diagram") + theme(
plot.title.position = "plot",
plot.title = element_text(hjust = 0.5)
)

fit_BC <- euler(list(
"NMIBC" = BC_xl$Gene,
"Total proteome" = total_FC$SYMBOL2,
"Phosphoproteome" = phospho_FC$SYMBOL2
))
plot(fit_BC, quantities = TRUE, main = "Figure 5a: GDA Euler plot")

print(list_to_data_frame(fit_BC))
## # A tibble: 38 × 8
## key ellipses original.values fitted.values residuals regionError diagError
## <dbl> <lgl> <lgl> <lgl> <lgl> <lgl> <lgl>
## 1 -9.75 FALSE FALSE FALSE FALSE FALSE FALSE
## 2 10.4 FALSE FALSE FALSE FALSE FALSE FALSE
## 3 5.20 FALSE FALSE FALSE FALSE FALSE FALSE
## 4 -16.2 FALSE FALSE FALSE FALSE FALSE FALSE
## 5 -16.2 FALSE FALSE FALSE FALSE FALSE FALSE
## 6 7.93 FALSE FALSE FALSE FALSE FALSE FALSE
## 7 11.9 FALSE FALSE FALSE FALSE FALSE FALSE
## 8 9.59 FALSE FALSE FALSE FALSE FALSE FALSE
## 9 20.5 FALSE FALSE FALSE FALSE FALSE FALSE
## 10 1.85 FALSE FALSE FALSE FALSE FALSE FALSE
## # ℹ 28 more rows
## # ℹ 1 more variable: stress <lgl>
From this table, there is a missing overlap region in all three sets
that the Euler diagram could not resolve, as indicated by the non-zero
residuals. In this case, the Venn diagram is the better choice.
# Print the list of proteins in each intersection
cat(paste("**NMIBC/Phospho:** ", paste(intersect(BC_xl$Gene, phospho_FC$SYMBOL2), collapse = ", ")))
NMIBC/Phospho: MKI67, EZH2, TGFBI, EIF4EBP1, PSMD9,
NUMA1, TMED7-TICAM2, TMED7, ARID1A, UVRAG, DHCR24, SULT1A1, RAF1, SIRT6,
PKM, CMAS, ATP7A, BAP1, CD163, KIF20B, HDAC4, CDC20, RING1, BST2,
TP53BP1, TSC2, ENG, DECR1, EPHB2, SATB2, CEACAM5, HDAC6, HDAC5, CRYZ,
CSTF2, CHEK2, NACC1, ATM, NFATC1, NOTCH3, HLA-A, HMOX1, GSTM1, KPNA2
cat(paste("**NMIBC/Total:** ", paste(intersect(BC_xl$Gene, total_FC$SYMBOL2), collapse = ", ")))
NMIBC/Total: ZNRD2, DCTN6, MMP9, SULT1A1, PECAM1,
BST2, TLR2, TARBP2, NFKB1, HMOX1
cat(paste("**Total/Phospho:** ", paste(intersect(total_FC$SYMBOL2, phospho_FC$SYMBOL2), collapse = ", ")))
Total/Phospho: OSBPL11, PARP14, DERL1, LSM3, OAS1,
SNRPG, STAT2, INPP5F, RNF213, SEPHS1, FKBP8, IFI16, MOB1B, ALDH2, BST2,
SLFN5, UBE2L6, VKORC1, DDI2, EYA3, LYSMD2, RRM1, PKIB, HMOX1, SIRPA,
CMPK2, HELZ2, TAP1, TAP2, SNRPB, EIF2AK2, UBE2D3, TAPBP, DTX3L, CNP,
SIRT2, TOR1AIP2, IFI44, SULT1A1, HMBS, SRSF10, FTL, ANKZF1, SERPINB1,
PNPT1, PML, PPP1R18, ATG16L1, MAT2B, GRN, MTMR2, CHMP7, ACOX1, PEA15,
NUP37, RAB13, RTN1, LIPA, MBD3, EMILIN2, RBBP6
cat(paste("**NMIBC/Phospho/Total:** ", paste(intersect(intersect(BC_xl$Gene, phospho_FC$SYMBOL2), total_FC$SYMBOL2), collapse = ", ")))
NMIBC/Phospho/Total: SULT1A1, BST2, HMOX1
Choudhary, Eira, C. Korin Bullen, Renu Goel, Alok Kumar Singh, Monali
Praharaj, Preeti Thakur, Rohan Dhiman, William R. Bishai, and Nisheeth
Agarwal. 2020.
“Relative and Quantitative Phosphoproteome Analysis
of Macrophages in Response to Infection by Virulent and Avirulent
Mycobacteria Reveals a Distinct Role of the
Cytosolic RNA Sensor RIG-I in Mycobacterium
Tuberculosis Pathogenesis.” Journal of Proteome
Research 19 (6): 2316–36.
https://doi.org/10.1021/acs.jproteome.9b00895.
Schaefer, Zoe, John Iradukunda, Evelyn N. Lumngwena, Kari B. Basso,
Jonathan M. Blackburn, and Ivana K. Parker. 2024.
“Multilevel
Proteomics Reveals Epigenetic Signatures in BCG-Mediated Macrophage
Activation.” Molecular & Cellular Proteomics 23
(11): 100851.
https://doi.org/10.1016/j.mcpro.2024.100851.
Yuan, Zuo-Fei, Simone Sidoli, Dylan M. Marchione, Johayra Simithy, Kevin
A. Janssen, Mary R. Szurgot, and Benjamin A. Garcia. 2018.
“EpiProfile 2.0: A Computational Platform for Processing
Epi-Proteomics Mass Spectrometry Data.” Journal of Proteome
Research 17 (7): 2533–41.
https://doi.org/10.1021/acs.jproteome.8b00133.
LS0tCnRpdGxlOiAiTXVsdGktb21pY3MgZGF0YSBhbmFseXNpcyIKYXV0aG9yOiAiWm9lIFNjaGFlZmVyIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgZGZfcHJpbnQ6IGthYmxlCmJpYmxpb2dyYXBoeTogcmVmZXJlbmNlcy5iaWIKLS0tCgpUaGlzIHBpcGVsaW5lIGlzIGludGVuZGVkIGZvciB1c2Ugd2l0aCB0aHJlZSBsZXZlbHMgb2YgcHJvdGVvbWljcyBkYXRhOiB0b3RhbCBwcm90ZW9tZSwgcGhvc3Bob3Byb3Rlb21lLCBhbmQgaGlzdG9uZSBwcm90ZW9tZS4gU2VlIEBTY2hhZWZlcjIwMjQgZm9yIG1vcmUgZGV0YWlsIG9uIHRoZSB0b3RhbCBhbmQgaGlzdG9uZSBwcm90ZW9tZTsgcGhvc3Bob3Byb3Rlb21lIGRhdGEgaXMgZnJvbSBAY2hvdWRoYXJ5MjAyMC4gVG90YWwgYW5kIHBob3NwaG9wcm90ZW9tZSBkYXRhIGFyZSBkZXNpZ25lZCB0byBiZSBpbnB1dCBhcyBhbiBFeGNlbCBzaGVldCBmcm9tIFByb3Rlb21lIERpc2NvdmVyZXIsIGFuZCB0aGUgaGlzdG9uZSBkYXRhIGFyZSB0aGUgYWdncmVnYXRlZCByYXRpb3MgZnJvbSBFcGlQcm9maWxlIChAWXVhbjIwMTgpLgoKRm9yIHRoZSByZWFkZXIsIGNvZGUgY2h1bmtzIGFyZSBjb2xsYXBzaWJsZSBpbi10ZXh0LiBUaGUgZnVsbCBSIE1hcmtkb3duIHNvdXJjZSBkb2N1bWVudCBjYW4gYWxzbyBiZSBkb3dubG9hZGVkIGZyb20gdGhpcyBwYWdlIGJ5IGNsaWNraW5nIHRoZSAiQ29kZSIgZHJvcGRvd24gYWJvdmUuIEFkZGl0aW9uYWxseSwgd2UgaGF2ZSBtYWRlIGF2YWlsYWJsZSB0aGUgZmlsZXMgZ2VuZXJhdGVkIGZyb20gb3VyIGRhdGEgYXQgdGhlIHByb2plY3QgcmVwb3NpdG9yeSBvbiBHaXRodWIuIE90aGVyIGZpbGVzIGNhbiBiZSBkb3dubG9hZGVkIGZyb20gdGhlaXIgcmVzcGVjdGl2ZSBzb3VyY2VzLgoKYGBge3Igc2V0dXB9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvID0gVFJVRSwgdGlkeSA9ICJzdHlsZXIiLCBmaWcuYXNwID0gMC42CikKCmxpYnJhcnkocGFjbWFuKQpwX2xvYWQoCiAgRW5oYW5jZWRWb2xjYW5vLCByZWFkeGwsIHRpZHl2ZXJzZSwgbGludHIsIEhvbW8uc2FwaWVucywgRE9TRSwgZGF0YS50YWJsZSwKICBjbHVzdGVyUHJvZmlsZXIsIGVucmljaHBsb3QsIGdndmVubiwgT3JnYW5pc21EYmksIGV1bGVyciwgaHR0cGdkLCB2aXJpZGlzLCBzY2FsZXMKKQoKdGhlbWVfcGlwZWxpbmUgPC0gdGhlbWVfc2V0KHRoZW1lX2dyYXkoKSArIHRoZW1lKAogIHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsCiAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpKQpgYGAKCiMgUHJvdGVvbWUgZGF0YSBwcm9jZXNzaW5nCgpUaGlzIHNlY3Rpb24gaXMgZGVzaWduZWQgdG8gd29yayB3aXRoIG5vcm1hbGl6ZWQgYWJ1bmRhbmNlIGRhdGEgZnJvbSBQcm90ZW9tZSBEaXNjb3ZlcmVyLiBUaGUgYHRvdGFsX3BhdGhgIHZhcmlhYmxlIHNob3VsZCBwb2ludCBkaXJlY3RseSB0byB0aGUgRXhjZWwgZmlsZSBjb250YWluaW5nIHRoZSBkYXRhIHlvdSB3b3VsZCBsaWtlIHRvIHVzZS4gVGhlIHJhbmdlIGlkZW50aWZpZWQgaW4gdGhlIGByZWFkX3hsc3hgIGNvbW1hbmQgc2hvdWxkIGNvbnRhaW4gY29sdW1ucyBmcm9tICJBY2Nlc3Npb24iIHRvIHRoZSBmaW5hbCAiRm91bmQgaW4gU2FtcGxlOiIgY29sdW1uLgoKYGBge3IgdG90YWwtZGF0YS1pbXBvcnR9CiMgUmVhZCBpbiBub3JtYWxpemVkIGFidW5kYW5jZSBkYXRhIHN0YXJ0aW5nIHdpdGggQWNjZXNzaW9uIGNvbHVtbgp0b3RhbF9wYXRoIDwtICJ+L0RvY3VtZW50cy9SLzMzMDYwX0NvbnRyb2xfdnNfSW5mZWN0aW9uX0pNSV9hYnVuZGFuY2VzX25vcm1hbGl6ZWRhYnVuZGFuY2VzLnhsc3giCnRvdGFsX0tCIDwtIHJlYWRfeGxzeChwYXRoID0gdG90YWxfcGF0aCwgcmFuZ2UgPSBjZWxsX2NvbHMoYygiRDpCSSIpKSkKCnRvdGFsX0tCIDwtIHNlbGVjdCgKICB0b3RhbF9LQiwKICBjKCJBY2Nlc3Npb24iLAogICAgc3RhcnRzX3dpdGgoCiAgICAgIGMoIkFidW5kYW5jZSBSYXRpbyAobG9nMik6IiwgIkFidW5kYW5jZSBSYXRpbyBBZGouIFAtVmFsdWU6IiwgIkZvdW5kIGluIFNhbXBsZToiKQogICAgICApCiAgICApCiAgKQpgYGAKYGBge3IgdG90YWwta2V5LW1hcCwgbWVzc2FnZT1GQUxTRX0KIyBSZXRyaWV2ZSBrZXlzIGFuZCBhc3NpZ24gc3ltYm9sIHRvIG51bWVyaWNhbCBFbnRyZXogSUQKIyAoS0VHRyBtYXBwaW5nIGdpdmVzIGFuIGVycm9yIGlmIHRoaXMgaXNuJ3QgaW5jbHVkZWQpCiMgRXhhbXBsZTogZ2l2ZW4gRW50cmV6IElEIDMwNDMsIGFzc2lnbiBVbmlwcm90IGFjY2Vzc2lvbiBudW1iZXIgUDY4ODcxLAojIHRoZW4gbWFwIHRoZSBnZW5lIHN5bWJvbCBIQkIgZm9yIGVhc2llciBpbnRlcnByZXRhdGlvbiBsYXRlci4KcmVmbGlzdCA8LSBPcmdhbmlzbURiaTo6c2VsZWN0KAogIEhvbW8uc2FwaWVucywKICBrZXlzID0ga2V5cyhIb21vLnNhcGllbnMsa2V5dHlwZSA9ICJVTklQUk9UIiksCiAgY29sdW1ucyA9IGMoIkVOVFJFWklEIiwgIlNZTUJPTCIpLCBrZXl0eXBlID0gIlVOSVBST1QiKQp0b3RhbF9LQiA8LSBmdWxsX2pvaW4odG90YWxfS0IsIHJlZmxpc3QsIGJ5ID0gam9pbl9ieShBY2Nlc3Npb24gPT0gVU5JUFJPVCkpCgojIEdldHRpbmcgcmlkIG9mIHRoaXMgaHVnZSB2YXJpYWJsZSB0byBzYXZlIHNwYWNlCnJtKHJlZmxpc3QpCgojIERyb3AgaW52YWxpZCBkYXRhIChOQSksIGRyb3AgYW55IGR1cGxpY2F0ZSBjYXNlcywgcmVuYW1lIGNvbHVtbnMgZm9yIGNsYXJpdHkKdG90YWxfS0IgPC0gdG90YWxfS0IgJT4lCiAgZHJvcF9uYShTWU1CT0wpICU+JQogIGRpc3RpbmN0KFNZTUJPTCwgLmtlZXBfYWxsID0gVFJVRSkgJT4lCiAgcmVuYW1lKCJsb2cyKEZDKSIgPSBzdGFydHNfd2l0aCgiQWJ1bmRhbmNlIFJhdGlvIChsb2cyKSIpLAogICAgICAgICAiUCB2YWx1ZSIgPSBzdGFydHNfd2l0aCgiQWJ1bmRhbmNlIFJhdGlvIEFkai4gUC1WYWx1ZSIpCikKCiMgUmVtb3ZlIHBvaW50cyB3aXRoIGludmFsaWQgUCB2YWx1ZXMgKGhvcml6b250YWwgbGluZSBhdCB0aGUgdG9wIG9mIGEgcGxvdCksCiMgYXNzaWduIHN5bWJvbHMgdG8gcm93IG5hbWVzCnRvdGFsX0tCIDwtIHRvdGFsX0tCICU+JQogIGZpbHRlcihgUCB2YWx1ZWAgPiA1LjNlLTE2KSAlPiUKICBtdXRhdGUoIlNZTUJPTDIiID0gYFNZTUJPTGApICU+JSAKICBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gIlNZTUJPTCIpCgojIFNldCB0aHJlc2hvbGQgdmFsdWVzIGZvciBwcm90ZW9tZSBhbmFseXNpcwp0b3RhbF9wX3RocmVzaG9sZCA8LSAwLjA1CnRvdGFsX0ZDX3RocmVzaG9sZCA8LSAwLjUKCiMgU3Vic2V0IHRoZSBkYXRhIGZvciBwbG90dGluZyBsYXRlciAtIGFueXRoaW5nIHdpdGggUCA8IHRocmVzaG9sZAp0b3RhbF9zaWduaWZpY2FudCA8LSBmaWx0ZXIodG90YWxfS0IsIGBQIHZhbHVlYCA8IHRvdGFsX3BfdGhyZXNob2xkKQpgYGAKCiMjIFZvbGNhbm8KCkZpZ3VyZSAxLjFhIGlzIGEgZ2VuZXJhbCB2b2xjYW5vIHBsb3QsIHNob3dpbmcgYSB2aXN1YWxpemF0aW9uIG9mIHRoZSBmdWxsIGRhdGFzZXQuCgpUaGUgYG1heC5vdmVybGFwc2AgcGFyYW1ldGVyIGluIHRoZSBgZ2VvbV90ZXh0X3JlcGVsYCBjYWxsIHNob3VsZCBiZSBjYXJlZnVsbHkgYWRqdXN0ZWQuIFRoZSBwbG90IG1heSBoYXZlIHRvIGJlIGdlbmVyYXRlZCBtdWx0aXBsZSB0aW1lcyB0byBnZXQgYW4gYXBwcm9wcmlhdGUgbGFiZWxpbmcgY291bnQuIFNldHRpbmcgdGhpcyB2YWx1ZSB0byBgSW5mYCBpcyBwb3NzaWJsZSwgYnV0IHdpbGwgbGlrZWx5IG92ZXJ3aGVsbSB0aGUgcGxvdCBhbmQgbm8gcG9pbnRzIHdpbGwgYmUgdmlzaWJsZS4KCmBgYHtyIHRvdGFsLXZvbGNhbm8tcGxvdH0KIyBHZW5lcmF0aW5nIG92ZXJhbGwgdm9sY2FubyBwbG90CiMgU2V0dGluZyBjYXRlZ29yaWVzIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbjoKIyBub3Qgc2lnbmlmaWNhbnQsIFAtdmFsdWUgb25seSwgRkMgb25seSwgYm90aAp0b3RhbF9LQiREaWZmRXhwIDwtIGNhc2Vfd2hlbigKICBhYnModG90YWxfS0IkYGxvZzIoRkMpYCkgPj0gdG90YWxfRkNfdGhyZXNob2xkICYKICAgIHRvdGFsX0tCJGBQIHZhbHVlYCA+IHRvdGFsX3BfdGhyZXNob2xkIH4gIkZDIG9ubHkiLAogIGFicyh0b3RhbF9LQiRgbG9nMihGQylgKSA8IHRvdGFsX0ZDX3RocmVzaG9sZCAmCiAgICB0b3RhbF9LQiRgUCB2YWx1ZWAgPD0gdG90YWxfcF90aHJlc2hvbGQgfiAiUCB2YWwuIG9ubHkiLAogIGFicyh0b3RhbF9LQiRgbG9nMihGQylgKSA+PSB0b3RhbF9GQ190aHJlc2hvbGQgJgogICAgdG90YWxfS0IkYFAgdmFsdWVgIDw9IHRvdGFsX3BfdGhyZXNob2xkIH4gIkZDIGFuZCBQIHZhbC4iLAogIC5kZWZhdWx0ID0gIk5TIgopCnRvdGFsX0tCJERpZmZFeHAgPC0gZmFjdG9yKHRvdGFsX0tCJERpZmZFeHAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkZDIGFuZCBQIHZhbC4iLCAiUCB2YWwuIG9ubHkiLCAiRkMgb25seSIsICJOUyIpKQoKIyBTZXR0aW5nIGFscGhhIHZhbHVlcyBhbmQgbGFiZWxzIHNvIHRoZSBzaWduaWZpY2FudCBwb2ludHMgYXJlIG1vc3QgdmlzaWJsZQp0b3RhbF9LQiRERUFscGhhIDwtIDAuMwp0b3RhbF9LQiRERUFscGhhW2Ficyh0b3RhbF9LQiRgbG9nMihGQylgKSA+PSB0b3RhbF9GQ190aHJlc2hvbGQgJgogICAgICAgICAgICAgICAgICAgdG90YWxfS0IkYFAgdmFsdWVgIDw9IHRvdGFsX3BfdGhyZXNob2xkXSA8LSAwLjgKCnRvdGFsX0tCJGxhYmVscyA8LSBpZl9lbHNlKAogIGFicyh0b3RhbF9LQiRgbG9nMihGQylgKSA+PSB0b3RhbF9GQ190aHJlc2hvbGQgJgogICAgdG90YWxfS0IkYFAgdmFsdWVgIDw9IHRvdGFsX3BfdGhyZXNob2xkLCB0b3RhbF9LQiRTWU1CT0wyLCAiIikKCmdncGxvdCh0b3RhbF9LQiwgYWVzKHggPSBgbG9nMihGQylgLCB5ID0gLWxvZzEwKGBQIHZhbHVlYCkpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBEaWZmRXhwLCBhbHBoYSA9IERFQWxwaGEpKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYyh0b3RhbF9GQ190aHJlc2hvbGQsIC10b3RhbF9GQ190aHJlc2hvbGQpLAogICAgICAgICAgICAgYWxwaGEgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAodG90YWxfcF90aHJlc2hvbGQpLAogICAgICAgICAgICAgYWxwaGEgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gbGFiZWxzKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgc2l6ZSA9IHJlbCgzKSkgKwogIGd1aWRlcyhhbHBoYSA9ICJub25lIikgKwogIGxhYnMoeCA9ICJsb2cyKEZvbGQgQ2hhbmdlKSIsIHkgPSAiLWxvZzEwKFAgdmFsdWUpIiwKICAgICAgIHRpdGxlID0gIkZpZ3VyZSAxLjFhOiBWb2xjYW5vIHBsb3QiLAogICAgICAgY29sb3IgPSAiQ2F0ZWdvcnkiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgKICAgIGFlc3RoZXRpY3MgPSAiY29sb3IiLCBvcHRpb24gPSAibWFnbWEiLAogICAgYmVnaW4gPSAwLjIsIGVuZCA9IDAuOQogICkKYGBgCgpJZiB0aGVyZSBhcmUga25vd24gZ2VuZXMgb2YgaW50ZXJlc3QgdG8gYmUgaW5jbHVkZWQgaW4gYSBwbG90LCBGaWd1cmUgMS4xYiBwbG90cyB0aGVzZSBwb2ludHMgb24gdG9wIG9mIHRoZSBmdWxsIGRhdGFzZXQgYXMgYSBiYWNrZ3JvdW5kLgoKYGBge3IgdG90YWwtZ3JvdXBpbmd9CiMgRGVmaW5pbmcga25vd24gZ2VuZXMgb2YgaW50ZXJlc3QgZm9yIGEgc2Vjb25kIHBsb3QKR3JvdXAxIDwtIGMoIk1PUkY0TDEiLCAiU0FNU04xIiwgIkRUWDNMIiwgIk1CRDMiLCAgIkhTRjEiLCAiRVlBMyIsCiAgICAgICAgICAgICAgICJOQ0FQRDIiLCAiVEFGOSIsICJQUFA0QyIsICJaTkY2MzgiKQpHcm91cDIgPC0gYygiQ0QyNzYiLCAiQ0RLNiIsICJDRDkiLCAiQ0Q4MiIsICJORktCMSIsICJCU1QyIiwgIkNUU0giKQoKIyBBc3NpZ25pbmcgc2hhcGVzIGFuZCBsYWJlbHMgZm9yIGVhY2ggZ3JvdXAgaW4gdGhlIHZvbGNhbm8gcGxvdCwKIyBsZWF2aW5nIHBvaW50cyB1bmxhYmVsZWQgaWYgb3V0c2lkZSB0aGUgZ3JvdXBzCnRvdGFsX0tCJENhdFNoYXBlcyA8LSBjYXNlX3doZW4oCiAgdG90YWxfS0IkU1lNQk9MMiAlaW4lIEdyb3VwMSB+ICIxIiwKICB0b3RhbF9LQiRTWU1CT0wyICVpbiUgR3JvdXAyIH4gIjIiLAogIC5kZWZhdWx0ID0gIjIwIgopCgp0b3RhbF9LQiRDYXROYW1lcyA8LSBjYXNlX3doZW4oCiAgdG90YWxfS0IkU1lNQk9MMiAlaW4lIEdyb3VwMSB8IHRvdGFsX0tCJFNZTUJPTDIgJWluJSBHcm91cDIgfiB0b3RhbF9LQiRTWU1CT0wyLAogIC5kZWZhdWx0ID0gIiIKKQpgYGAKCmBgYHtyIHRvdGFsLWdyb3VwZWQtdm9sY2Fuby1wbG90fQojIEdlbmVyYXRpbmcgdm9sY2FubyBwbG90CiMgYXJyYW5nZSgpIGNvbW1hbmQgc29ydHMgdGhlIG5hbWVkIHBvaW50cyBvbiB0b3Agc28gdGhleSBhcmVuJ3QgYnVyaWVkIHVuZGVybmVhdGgKZ2dwbG90KGFycmFuZ2UodG90YWxfS0IsIENhdE5hbWVzKSwgYWVzKHggPSBgbG9nMihGQylgLCB5ID0gLWxvZzEwKGBQIHZhbHVlYCkpKSArCiAgZ2VvbV9wb2ludChhZXMoc2hhcGUgPSBDYXRTaGFwZXMsIGNvbG9yID0gQ2F0U2hhcGVzKSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGModG90YWxfRkNfdGhyZXNob2xkLCAtdG90YWxfRkNfdGhyZXNob2xkKSwgYWxwaGEgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAodG90YWxfcF90aHJlc2hvbGQpLCBhbHBoYSA9IDAuNSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBDYXROYW1lcyksIHNpemUgPSByZWwoMyksIHNob3cubGVnZW5kID0gRkFMU0UsIG1heC5vdmVybGFwcyA9IEluZikgKwogIGd1aWRlcyhhbHBoYSA9ICJub25lIiwgc2hhcGUgPSAibm9uZSIpICsKICBsYWJzKHggPSAibG9nMihGb2xkIENoYW5nZSkiLCB5ID0gIi1sb2cxMChQIHZhbHVlKSIsCiAgICAgICB0aXRsZSA9ICJGaWd1cmUgMS4xYjogVm9sY2FubyBwbG90IHdpdGggZ3JvdXBzIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoIkNhdGVnb3J5IiwKICAgIGFlc3RoZXRpY3MgPSAiY29sb3IiLCBvcHRpb24gPSAibWFnbWEiLAogICAgYmVnaW4gPSAwLjIsIGVuZCA9IDAuOSwKICAgIGxhYmVscyA9IGMoIkdyb3VwIDIiLCAiR3JvdXAgMSIsICJOb25lIikKICApCmBgYAoKIyMgUGF0aHdheSBlbnJpY2htZW50CgpUaGlzIHNlY3Rpb24gcGVyZm9ybXMgYSBwYXRod2F5IGFuYWx5c2lzIG9uIHRoZSBzaWduaWZpY2FudCBwcm90ZWlucyBhbG9uZSB0byByZWR1Y2UgY29tcHV0YXRpb25hbCBsb2FkIHdoZW4gY2FsbGluZyBgZW5yaWNoR09gIGFuZCBgZW5yaWNoS0VHR2AuIFRoZXNlIHZhcmlhYmxlcyBjYW4gYmUgZWFzaWx5IGFkanVzdGVkIHRvIGFuYWx5emUgYWxsIHByb3RlaW5zIHRvZ2V0aGVyIGlmIGRlc2lyZWQuCgpJbiBvdXIgc291cmNlIGRhdGFzZXQsIHRoZXJlIHdlcmUgbm8gZG93bnJlZ3VsYXRlZCBLRUdHIHBhdGh3YXlzLiBUaGlzIGNhdXNlcyBlcnJvciBtZXNzYWdlcywgc28gdGhlICJUb3RhbCBLRUdHIGRvd24iIHBsb3QgaGFzIGJlZW4gcmVtb3ZlZC4gVGhlIGNhbGwgdG8gYGxhYmVsc2AgYmVsb3cgaWxsdXN0cmF0ZXMgYSBkaWZmaWN1bHR5IHdpdGggcGxvdHRpbmcgbG9uZyBvbnRvbG9neSB0ZXJtcywgYnV0IHRoZSBgbGFiZWxfZm9ybWF0YCBwYXJhbWV0ZXIgaXMgYW4gZWFzeSB3YXkgdG8gc29sdmUgdGhpcy4gT3V0c2lkZSBvZiBgZW5yaWNocGxvdDo6ZG90cGxvdGAsIGNhbGxpbmcgYGxhYmVsX3dyYXBgIGZyb20gYGxhYmVsc2AgYW5kIHBhc3NpbmcgYSBjaGFyYWN0ZXIgY291bnQgdG8gd3JhcCBhdCBpcyBhIGdvb2Qgc29sdXRpb24uIFdlIGFsc28gc2hvdyBhIHdheSB0byBtYW51YWxseSByZXBsYWNlIGxvbmcgbmFtZXMgbGF0ZXIgaW4gdGhpcyBzZWN0aW9uLgoKYGBge3IgdG90YWwtZW5yaWNofQojIFNlcGFyYXRlIHRvdGFsX3NpZ25pZmljYW50IHN1YnNldCBpbnRvIHVwcmVndWxhdGVkIHZzIGRvd25yZWd1bGF0ZWQKdG90YWxfc2lnbmlmaWNhbnRfdXAgPC0gZmlsdGVyKHRvdGFsX3NpZ25pZmljYW50LCBgbG9nMihGQylgID4gMCkKdG90YWxfc2lnbmlmaWNhbnRfZG93biA8LSBmaWx0ZXIodG90YWxfc2lnbmlmaWNhbnQsIGBsb2cyKEZDKWAgPCAwKQoKIyBFbnJpY2hpbmcgZm9yICp0b3RhbCogR08gdGVybXMgLSBvcHRpb25hbCwgY2FuIGJlIHVzZWQgdG8gdmlzdWFsaXplIG92ZXJhbGwgcmVzdWx0cwp0b3RhbF9HT191cCA8LSBlbnJpY2hHTyh0b3RhbF9zaWduaWZpY2FudF91cCRFTlRSRVpJRCwgIm9yZy5Icy5lZy5kYiIpCnRvdGFsX0dPX2Rvd24gPC0gZW5yaWNoR08odG90YWxfc2lnbmlmaWNhbnRfZG93biRFTlRSRVpJRCwgIm9yZy5Icy5lZy5kYiIpCgojIEVucmljaCBmb3IgS0VHRyBwYXRod2F5cwp0b3RhbF9LRUdHX3VwIDwtIGVucmljaEtFR0codG90YWxfc2lnbmlmaWNhbnRfdXAkRU5UUkVaSUQpCnRvdGFsX0tFR0dfZG93biA8LSBlbnJpY2hLRUdHKHRvdGFsX3NpZ25pZmljYW50X2Rvd24kRU5UUkVaSUQpCgojIGRvdHBsb3QoKSBjYW4gYmUgdXNlZCBmb3IgYW4gb3ZlcnZpZXcgb2YgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBrZXkgdGVybXMKZG90cGxvdCh0b3RhbF9LRUdHX3VwLCBzaG93Q2F0ZWdvcnkgPSAyMCwgZm9udC5zaXplID0gcmVsKDEpLAogICAgICAgIHRpdGxlID0gIkZpZ3VyZSAxLjJhOiBUb3RhbCBLRUdHIHVwIikgKwogIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpkb3RwbG90KHRvdGFsX0dPX3VwLCBzaG93Q2F0ZWdvcnkgPSAyMCwgZm9udC5zaXplID0gcmVsKDEpLAogICAgICAgIHRpdGxlID0gIkZpZ3VyZSAxLjJiOiBUb3RhbCBHTyB1cCAodW53cmFwcGVkKSIpICsKICB0aGVtZShwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKZG90cGxvdCh0b3RhbF9HT191cCwgc2hvd0NhdGVnb3J5ID0gMjAsIGZvbnQuc2l6ZSA9IHJlbCgxKSwKICAgICAgICB0aXRsZSA9ICJGaWd1cmUgMS4yYzogVG90YWwgR08gdXAgKHdyYXBwZWQpIiwgbGFiZWxfZm9ybWF0ID0gNjApICsKICB0aGVtZShwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKZG90cGxvdCh0b3RhbF9HT19kb3duLCBzaG93Q2F0ZWdvcnkgPSAyMCwgZm9udC5zaXplID0gcmVsKDEpLAogICAgICAgIHRpdGxlID0gIkZpZ3VyZSAxLjJkOiBUb3RhbCBHTyBkb3duIikgKwogIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpgYGAKCkZvciBlYWNoIHN1YnNldCBvZiBkYXRhLCB0aGUgdG9wIDEwIHRlcm1zIGFyZSBmb3VuZCBpbiBlYWNoIEdPIGFzcGVjdCAobW9sZWN1bGFyIGZ1bmN0aW9uIChNRiksIGJpb2xvZ2ljYWwgcHJvY2VzcyAoQlApLCBjZWxsdWxhciBjb21wb25lbnQgKENDKSkuIFRvIGluY2x1ZGUgbW9yZSB0ZXJtcywgYWRqdXN0IHRoZSBgbiA9IDEwYCBwYXJhbWV0ZXIgaW4gdGhlIGNhbGxzIHRvIGBzbGljZV9taW4oKWAuCgpJbiB0aGlzIHNlY3Rpb24sIHdlIGFsc28gcmV3b3JkIHNvbWUgdmVyeSBsb25nIEdPIHRlcm1zIHRvIGZpdCB0aGVtIGluIGFuIGFwcHJvcHJpYXRlLXNpemUgcGxvdC4gVGhpcyBsaXN0IHdpbGwgbmVlZCB0byBiZSBhZGp1c3RlZCBiYXNlZCBvbiB0aGUgZW5yaWNoZWQgdGVybXMgaW4gYW4gaW5kaXZpZHVhbCBkYXRhc2V0LgoKYGBge3IgdG90YWwtR08tdXAtc3BsaXR9CiMgU2VwYXJhdGluZyB0aGUgR08gdGVybXMgYnkgb250b2xvZ3kgYXNwZWN0CnRvdGFsX0dPX3VwX01GIDwtIGVucmljaEdPKHRvdGFsX3NpZ25pZmljYW50X3VwJEVOVFJFWklELAogICJvcmcuSHMuZWcuZGIiLCBvbnQgPSAiTUYiCiAgKQp0b3RhbF9HT191cF9CUCA8LSBlbnJpY2hHTyh0b3RhbF9zaWduaWZpY2FudF91cCRFTlRSRVpJRCwKICAib3JnLkhzLmVnLmRiIiwgb250ID0gIkJQIgogICkKdG90YWxfR09fdXBfQ0MgPC0gZW5yaWNoR08odG90YWxfc2lnbmlmaWNhbnRfdXAkRU5UUkVaSUQsCiAgIm9yZy5Icy5lZy5kYiIsIG9udCA9ICJDQyIKICApCmBgYAoKYGBge3IgdG90YWwtR08tdXAtY2xlYW59CiMgVGFraW5nIHRoZSB0b3AgMTAgZW5yaWNoZWQgR08gdGVybXMgaW4gZWFjaCBjYXRlZ29yeSBieSBQIHZhbHVlCnRvdGFsX0dPX3VwX3NsaWNlX01GIDwtIHNsaWNlX21pbigKICBmaWx0ZXIodG90YWxfR09fdXBfTUZAcmVzdWx0LCBwLmFkanVzdCA8IDAuMDUpLAogIG9yZGVyX2J5ID0gYHAuYWRqdXN0YCwgbiA9IDEwCikKdG90YWxfR09fdXBfc2xpY2VfQlAgPC0gc2xpY2VfbWluKAogIGZpbHRlcih0b3RhbF9HT191cF9CUEByZXN1bHQsIHAuYWRqdXN0IDwgMC4wNSksCiAgb3JkZXJfYnkgPSBgcC5hZGp1c3RgLCBuID0gMTAKKQp0b3RhbF9HT191cF9zbGljZV9DQyA8LSBzbGljZV9taW4oCiAgZmlsdGVyKHRvdGFsX0dPX3VwX0NDQHJlc3VsdCwgcC5hZGp1c3QgPCAwLjA1KSwKICBvcmRlcl9ieSA9IGBwLmFkanVzdGAsIG4gPSAxMAopCiMgQ29tYmluaW5nIHRvcCAxMCB0ZXJtcyBpbiBlYWNoIGFzcGVjdAp0b3RhbF9HT191cF9zbGljZV9BTEwgPC0gYmluZF9yb3dzKAogICJNRiIgPSB0b3RhbF9HT191cF9zbGljZV9NRiwKICAiQlAiID0gdG90YWxfR09fdXBfc2xpY2VfQlAsCiAgIkNDIiA9IHRvdGFsX0dPX3VwX3NsaWNlX0NDLAogIC5pZCA9ICJPbnRvbG9neSIKKQp0b3RhbF9HT191cF9zbGljZV9BTEwkR1IgPC0gcGFyc2VfcmF0aW8odG90YWxfR09fdXBfc2xpY2VfQUxMJEdlbmVSYXRpbykKCiMgQWRkZWQgaW4gdG8gc2hvcnRlbiBuYW1lcyB3aGVuIHBsb3R0ZWQgLSB0aGlzIGNhbiBiZSByZW1vdmVkIG9yIGFkanVzdGVkCnRvdGFsX0dPX3VwX3NsaWNlX0FMTFsiRGVzY3JpcHRpb24iXVt0b3RhbF9HT191cF9zbGljZV9BTExbIkRlc2NyaXB0aW9uIl0gPT0gImh5ZHJvbGFzZSBhY3Rpdml0eSwgYWN0aW5nIG9uIGNhcmJvbi1uaXRyb2dlbiAoYnV0IG5vdCBwZXB0aWRlKSBib25kcyJdIDwtICJoeWRyb2xhc2UsIEMtTiBub24tcGVwdGlkZSBib25kcyIKdG90YWxfR09fdXBfc2xpY2VfQUxMWyJEZXNjcmlwdGlvbiJdW3RvdGFsX0dPX3VwX3NsaWNlX0FMTFsiRGVzY3JpcHRpb24iXSA9PSAiaHlkcm9sYXNlIGFjdGl2aXR5LCBhY3Rpbmcgb24gY2FyYm9uLW5pdHJvZ2VuIChidXQgbm90IHBlcHRpZGUpIGJvbmRzLCBpbiBjeWNsaWMgYW1pZGluZXMiXSA8LSAiaHlkcm9sYXNlLCBDLU4gbm9uLXBlcHRpZGUgYm9uZHMgaW4gY3ljbGljIGFtaWRpbmVzIgp0b3RhbF9HT191cF9zbGljZV9BTExbIkRlc2NyaXB0aW9uIl1bdG90YWxfR09fdXBfc2xpY2VfQUxMWyJEZXNjcmlwdGlvbiJdID09ICJhbnRpZ2VuIHByb2Nlc3NpbmcgYW5kIHByZXNlbnRhdGlvbiBvZiBlbmRvZ2Vub3VzIHBlcHRpZGUgYW50aWdlbiB2aWEgTUhDIGNsYXNzIEkiXSA8LSAiZW5kb2dlbm91cyBhbnRpZ2VuIHByb2Nlc3NpbmcgYW5kIHByZXNlbnRhdGlvbiAoTUhDIGNsYXNzIEkpIgp0b3RhbF9HT191cF9zbGljZV9BTExbIkRlc2NyaXB0aW9uIl1bdG90YWxfR09fdXBfc2xpY2VfQUxMWyJEZXNjcmlwdGlvbiJdID09ICJhbnRpZ2VuIHByb2Nlc3NpbmcgYW5kIHByZXNlbnRhdGlvbiBvZiBwZXB0aWRlIGFudGlnZW4gdmlhIE1IQyBjbGFzcyBJIl0gPC0gInBlcHRpZGUgYW50aWdlbiBwcm9jZXNzaW5nIGFuZCBwcmVzZW50YXRpb24gKE1IQyBjbGFzcyBJKSIKdG90YWxfR09fdXBfc2xpY2VfQUxMWyJEZXNjcmlwdGlvbiJdW3RvdGFsX0dPX3VwX3NsaWNlX0FMTFsiRGVzY3JpcHRpb24iXSA9PSAiYW50aWdlbiBwcm9jZXNzaW5nIGFuZCBwcmVzZW50YXRpb24gb2YgZW5kb2dlbm91cyBwZXB0aWRlIGFudGlnZW4iXSA8LSAiZW5kb2dlbm91cyBhbnRpZ2VuIHByb2Nlc3NpbmcgYW5kIHByZXNlbnRhdGlvbiIKdG90YWxfR09fdXBfc2xpY2VfQUxMWyJEZXNjcmlwdGlvbiJdW3RvdGFsX0dPX3VwX3NsaWNlX0FMTFsiRGVzY3JpcHRpb24iXSA9PSAiZXhvbnVjbGVhc2UgYWN0aXZpdHksIGFjdGl2ZSB3aXRoIGVpdGhlciByaWJvLSBvciBkZW94eXJpYm9udWNsZWljIGFjaWRzIGFuZCBwcm9kdWNpbmcgNSctcGhvc3Bob21vbm9lc3RlcnMiXSA8LSAiNSctZXhvbnVjbGVhc2VzIgoKCiMgUmVvcmRlcmluZyB0aGUgdG9wIHRlcm1zIGJ5IGdlbmUgcmF0aW8KdG90YWxfR09fdXBfc2xpY2VfQUxMJERlc2NyaXB0aW9uIDwtIGZjdF9yZW9yZGVyKAogIHRvdGFsX0dPX3VwX3NsaWNlX0FMTCREZXNjcmlwdGlvbiwgdG90YWxfR09fdXBfc2xpY2VfQUxMJEdSCikKYGBgCgpgYGB7ciB0b3RhbC1LRUdHLXVwLWVucmljaH0KIyBSZXBlYXQgZm9yIEtFR0cKIyBUYWtpbmcgdGhlIHRvcCAxMCBlbnJpY2hlZCB0ZXJtcyBieSBQIHZhbHVlCnRvdGFsX0tFR0dfdXBfc2xpY2UgPC0gc2xpY2VfbWluKGZpbHRlcih0b3RhbF9LRUdHX3VwQHJlc3VsdCwgcC5hZGp1c3QgPCAwLjA1KSwKICBvcmRlcl9ieSA9IGBwLmFkanVzdGAsIG4gPSAxMAopCiMgUmVvcmRlcmluZyB0aGUgdG9wIHRlcm1zIGJ5IGdlbmUgcmF0aW8KdG90YWxfS0VHR191cF9zbGljZSRHUiA8LSBwYXJzZV9yYXRpbyh0b3RhbF9LRUdHX3VwX3NsaWNlJEdlbmVSYXRpbykKdG90YWxfS0VHR191cF9zbGljZSREZXNjcmlwdGlvbiA8LSBmY3RfcmVvcmRlcigKICB0b3RhbF9LRUdHX3VwX3NsaWNlJERlc2NyaXB0aW9uLCB0b3RhbF9LRUdHX3VwX3NsaWNlJEdSCikKdG90YWxfS0VHR191cF9zbGljZSRPbnRvbG9neSA8LSAiS0VHRyIKYGBgCgpgYGB7ciB0b3RhbC11cC1jb21iaW5lfQojIENvbWJpbmluZyBHTyBhbmQgS0VHRyBmb3IgdXByZWd1bGF0ZWQgcHJvdGVpbnMKdG90YWxfdXBfYm91bmQgPC0gYmluZF9yb3dzKAogIHRvdGFsX0dPX3VwX3NsaWNlX0FMTCwgdG90YWxfS0VHR191cF9zbGljZQopCnRvdGFsX3VwX2JvdW5kJERlc2NyaXB0aW9uIDwtIGZjdF9yZW9yZGVyKAogIHRvdGFsX3VwX2JvdW5kJERlc2NyaXB0aW9uLCB0b3RhbF91cF9ib3VuZCRHUgopCgojIE1hbnVhbGx5IG9yZGVyaW5nIGZhY3RvcnMgdG8gZGlzcGxheSBhbGwgR08gYXNwZWN0cyB0b2dldGhlcgp0b3RhbF91cF9ib3VuZCRPbnRvbG9neSA8LSBmYWN0b3IodG90YWxfdXBfYm91bmQkT250b2xvZ3ksCiAgbGV2ZWxzID0gYygiQlAiLCAiQ0MiLCAiTUYiLCAiS0VHRyIpCikKYGBgCgpgYGB7ciB0b3RhbC11cC1wYXRod2F5cywgZmlnLmFzcCA9IDAuOX0KIyBHcmFwaGluZyBhbGwgNCBjYXRlZ29yaWVzIHRvZ2V0aGVyCmdncGxvdCh0b3RhbF91cF9ib3VuZCwKICAjIEdlbmUgcmF0aW8gb24gWCBheGlzLCB2ZXJib3NlIHBhdGh3YXkgZGVzY3JpcHRpb24gb24gWSBheGlzCiAgIyBDb2xvcmluZyBieSBhZGp1c3RlZCBQIHZhbHVlIGFuZCBzaXplIGJ5IHRoZSBjb3VudCBvZiBnZW5lcyBpbiBhIHBhdGh3YXkKICBhZXMoeCA9IEdSLCB5ID0gRGVzY3JpcHRpb24sIGNvbG9yID0gcC5hZGp1c3QsIHNpemUgPSBDb3VudCkKICApICsKICBzY2FsZV95X2Rpc2NyZXRlKGxhYmVscyA9IGxhYmVsX3dyYXAoNzApKSArCiAgZ2VvbV9jb3VudCgpICsKICBsYWJzKAogICAgeCA9ICJHZW5lIHJhdGlvIiwgeSA9ICJQYXRod2F5IiwKICAgIHRpdGxlID0gIkZpZ3VyZSAxLjJlOiBVcHJlZ3VsYXRlZCBwYXRod2F5cyIKICApICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoCiAgICBhZXN0aGV0aWNzID0gImNvbG9yIiwgb3B0aW9uID0gIm1hZ21hIiwKICAgIG5hbWUgPSAiQWRqdXN0ZWQgUCIsIGJlZ2luID0gMCwgZW5kID0gMC45CiAgKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMjUsIDI0KSkgKwogIGZhY2V0X2dyaWQoT250b2xvZ3kgfiAuLAogICAgZHJvcCA9IFRSVUUsIHNwYWNlID0gImZyZWVfeSIsIHNjYWxlcyA9ICJmcmVlX3kiCiAgKSArCiAgdGhlbWUoCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICBwYW5lbC5zcGFjaW5nID0gdW5pdCgwLjYsICJsaW5lcyIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgxLjEpKSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAibGlnaHQgZ3JheSIpLAogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5OTAiKQogICkKYGBgCgpgYGB7ciB0b3RhbC1HTy1kb3duLXNwbGl0fQojIFNhbWUgcHJvY2VzcyBhcyB0aGUgcHJldmlvdXMgY2h1bmsKIyBTZXBhcmF0aW5nIHRoZSBHTyB0ZXJtcyBieSBvbnRvbG9neSBhc3BlY3QKdG90YWxfR09fZG93bl9NRiA8LSBlbnJpY2hHTyh0b3RhbF9zaWduaWZpY2FudF9kb3duJEVOVFJFWklELAogICJvcmcuSHMuZWcuZGIiLCBvbnQgPSAiTUYiCikKdG90YWxfR09fZG93bl9CUCA8LSBlbnJpY2hHTyh0b3RhbF9zaWduaWZpY2FudF9kb3duJEVOVFJFWklELAogICJvcmcuSHMuZWcuZGIiLCBvbnQgPSAiQlAiCikKdG90YWxfR09fZG93bl9DQyA8LSBlbnJpY2hHTyh0b3RhbF9zaWduaWZpY2FudF9kb3duJEVOVFJFWklELAogICJvcmcuSHMuZWcuZGIiLCBvbnQgPSAiQ0MiCikKYGBgCgpgYGB7ciB0b3RhbC1kb3duLWNsZWFufQojIFRha2luZyB0aGUgdG9wIDEwIGVucmljaGVkIHRlcm1zIGluIGVhY2ggY2F0ZWdvcnkgYnkgUCB2YWx1ZQp0b3RhbF9HT19kb3duX3NsaWNlX01GIDwtIHNsaWNlX21pbigKICBmaWx0ZXIodG90YWxfR09fZG93bl9NRkByZXN1bHQsIHAuYWRqdXN0IDwgMC4wNSksCiAgb3JkZXJfYnkgPSBgcC5hZGp1c3RgLCBuID0gMTAKKQp0b3RhbF9HT19kb3duX3NsaWNlX0JQIDwtIHNsaWNlX21pbigKICBmaWx0ZXIodG90YWxfR09fZG93bl9CUEByZXN1bHQsIHAuYWRqdXN0IDwgMC4wNSksCiAgb3JkZXJfYnkgPSBgcC5hZGp1c3RgLCBuID0gMTAKKQp0b3RhbF9HT19kb3duX3NsaWNlX0NDIDwtIHNsaWNlX21pbigKICBmaWx0ZXIodG90YWxfR09fZG93bl9DQ0ByZXN1bHQsIHAuYWRqdXN0IDwgMC4wNSksCiAgb3JkZXJfYnkgPSBgcC5hZGp1c3RgLCBuID0gMTAKKQoKIyBDb21iaW5pbmcgdG9wIDEwIHRlcm1zIGluIGVhY2ggYXNwZWN0CnRvdGFsX2Rvd25fYm91bmQgPC0gYmluZF9yb3dzKAogICJNRiIgPSB0b3RhbF9HT19kb3duX3NsaWNlX01GLAogICJCUCIgPSB0b3RhbF9HT19kb3duX3NsaWNlX0JQLAogICJDQyIgPSB0b3RhbF9HT19kb3duX3NsaWNlX0NDLAogIC5pZCA9ICJPbnRvbG9neSIKKQp0b3RhbF9kb3duX2JvdW5kJEdSIDwtIHBhcnNlX3JhdGlvKHRvdGFsX2Rvd25fYm91bmQkR2VuZVJhdGlvKQoKIyBSZW9yZGVyaW5nIHRoZSB0b3AgdGVybXMgYnkgZ2VuZSByYXRpbwp0b3RhbF9kb3duX2JvdW5kJERlc2NyaXB0aW9uIDwtIGZjdF9yZW9yZGVyKAogIHRvdGFsX2Rvd25fYm91bmQkRGVzY3JpcHRpb24sIHRvdGFsX2Rvd25fYm91bmQkR1IKKQp0b3RhbF9kb3duX2JvdW5kJE9udG9sb2d5IDwtIGZhY3Rvcih0b3RhbF9kb3duX2JvdW5kJE9udG9sb2d5LAogIGxldmVscyA9IGMoIkJQIiwgIkNDIiwgIk1GIiwgIktFR0ciKQopCgojIEtFR0cgZW5yaWNobWVudCBkaWQgbm90IHNob3cgYW55dGhpbmcgc28gaXQgY2F1c2VkIGFuIGVycm9yIGluIG91ciBkYXRhIGFuZCBoYXMgYmVlbiByZW1vdmVkCiMgSWYgZGVzaXJlZCwgS0VHRyBlbnJpY2htZW50IGNhbiBiZSBwZXJmb3JtZWQgaGVyZSBhcyBpbiB0aGUgcHJldmlvdXMgY2h1bmsKYGBgCgpgYGB7ciB0b3RhbC1kb3duLXBhdGh3YXlzfQpnZ3Bsb3QodG90YWxfZG93bl9ib3VuZCwKICAgICAgIGFlcyh4ID0gR1IsIHkgPSBEZXNjcmlwdGlvbiwgY29sb3IgPSBwLmFkanVzdCwgc2l6ZSA9IENvdW50KSkgKwogIGdlb21fY291bnQoKSArCiAgbGFicygKICAgIHggPSAiR2VuZSByYXRpbyIsIHkgPSAiUGF0aHdheSIsCiAgICB0aXRsZSA9ICJGaWd1cmUgMS4yZjogRG93bnJlZ3VsYXRlZCBwYXRod2F5cyIKICApICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoCiAgICBhZXN0aGV0aWNzID0gImNvbG9yIiwgb3B0aW9uID0gIm1hZ21hIiwKICAgIG5hbWUgPSAiQWRqdXN0ZWQgUCIsIGJlZ2luID0gMCwgZW5kID0gMC45CiAgKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMjUsIDI0KSkgKwogIGZhY2V0X2dyaWQoT250b2xvZ3kgfiAuLAogICAgZHJvcCA9IFRSVUUsIHNwYWNlID0gImZyZWVfeSIsCiAgICBzY2FsZXMgPSAiZnJlZV95IgogICkgKwogIHRoZW1lKAogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgcGFuZWwuc3BhY2luZyA9IHVuaXQoMC42LCAibGluZXMiKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMS4xKSksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gImxpZ2h0IGdyYXkiKSwKICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTkwIikKICApCmBgYAoKRmlndXJlIDEuMmMgY2x1c3RlcnMgdGVybXMgYnkgZnVuY3Rpb24sIHdoaWNoIHNob3dzIGVucmljaGVkIGRvbWFpbnMuCgpgYGB7ciB0b3RhbC1tYXAsIGZpZy5hc3AgPSAxfQojIFBsb3R0aW5nIHRoZSBlbnJpY2htZW50IHBsb3QgZm9yIG9uZSBhc3BlY3Qgb2YgdXByZWd1bGF0ZWQgcGF0aHdheXMKIyBFbnJpY2ggZmlyc3Qgd2l0aCBwYWlyd2lzZV90ZXJtX3NpbSwgdGhlbiBnZW5lcmF0ZSB0aGUgcGxvdCBpdHNlbGYKdG90YWxfR09fdXBfTUYgPC0gcGFpcndpc2VfdGVybXNpbSh0b3RhbF9HT191cF9NRikKZW1hcHBsb3QodG90YWxfR09fdXBfTUYsIGxheW91dCA9ICJuaWNlbHkiLCBub2RlX2xhYmVsID0gIm5vbmUiKSArCiAgZ2d0aXRsZSgiRmlndXJlIDEuMmc6IFVwcmVndWxhdGVkIGVucmljaG1lbnQgcGxvdCAoTUYpIikgKwogIGdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBzdHJfd3JhcChuYW1lLCB3aWR0aCA9IDYwKSkpICsKICB0aGVtZShwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigxLCAxLCAxLCAxKSkKYGBgCgojIFBob3NwaG9wcm90ZW9tZSBkYXRhIHByb2Nlc3NpbmcKClRoaXMgc2VjdGlvbiBpcyB3cml0dGVuIHRvIGludGVycHJldCBwaG9zcGhvcHJvdGVvbWUgZGF0YS4gSGVyZSwgd2UgZG8gbm90IGFjY291bnQgZm9yIHBhcnRpY3VsYXIgcGhvc3Bob3J5bGF0aW9uIHNpdGVzIG9yIHRoZWlyIGZ1bmN0aW9ucy4gVGhlIG9yaWdpbmFsIGRhdGEgaXMgZnJvbSBAY2hvdWRoYXJ5MjAyMC4gQmVsb3cgaXMgYSByZXByb2Nlc3Npbmcgb2YgdGhpcyBkYXRhIHRvIGFsaWduIHdpdGggdGhlIHRvdGFsIGFuZCBoaXN0b25lIHByb3Rlb21lLgoKYGBge3IgcGhvc3Boby1pbXBvcnQsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIFJlYWQgaW4gcGhvc3Bob3Byb3Rlb21lIGRhdGEsIGFzc2lnbiBzeW1ib2wgdG8gcm93IG5hbWVzCiMgQWxyZWFkeSBmb3JtYXR0ZWQgc28gdGhlcmUncyBubyBuZWVkIHRvIG1hcCB3aXRoIHJlZmxpc3QKcGhvc3Bob19yYXcgPC0gcmVhZF94bHN4KCJ+L0RvY3VtZW50cy9SL1BYRF9yYXcueGxzeCIpCnBob3NwaG9fcmF3IDwtIHBob3NwaG9fcmF3ICU+JQogIG11dGF0ZSgiU1lNQk9MMiIgPSBgU1lNQk9MYCkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKHZhciA9ICJTWU1CT0wiKSAlPiUKICBkcm9wX25hKFNZTUJPTDIsIGBsb2cyKEZDKWAsIGBQIHZhbHVlYCkgJT4lCiAgZGlzdGluY3QoU1lNQk9MMiwgLmtlZXBfYWxsID0gVFJVRSkgJT4lCiAgbXV0YXRlKGAuLi42YCA9IE5VTEwpCgojIFNldCB0aHJlc2hvbGQgdmFsdWVzIGZvciBwcm90ZW9tZSBhbmFseXNpcwpwaG9zcGhvX3BfdGhyZXNob2xkIDwtIDAuMDUKcGhvc3Bob19GQ190aHJlc2hvbGQgPC0gMC41CgpwaG9zcGhvX3NpZ25pZmljYW50IDwtIGZpbHRlcihwaG9zcGhvX3JhdywgYFAgdmFsdWVgIDwgcGhvc3Bob19wX3RocmVzaG9sZCkKYGBgCgojIyBWb2xjYW5vCgpgYGB7ciBwaG9zcGhvLWdyb3VwaW5nfQojIFJlcGVhdGluZyB0aGUgcHJvY2Vzc2VzIGZyb20gYmVmb3JlCiMgSWYgeW91ciBncm91cHMgYXJlIHRvbyBsYXJnZSwgdGhleSB3aWxsIG5vdCBkaXNwbGF5IGFzIGFsbCBsYWJlbGVkIGF0IG9uY2UKR3JvdXAxX3Bob3NwaG8gPC0gYygKICAiSEpVUlAiLCAiQ0hBRjFCIiwgIlJCTDEiLCAiRVlBMyIsICJCUkNBMSIsCiAgIk5DT0E2IiwgIlBBTEIyIiwgIkJBUkQxIiwgIlJFUFMyIiwgIlpORjUxMSIsICJCUldEMSIsCiAgIkwzTUJUTDMiLCAiV0RSNzYiLCAiQ0hBRjFBIiwgIlBJQVMyIiwgIlJVTlgxIiwgIlJCMSIsICJKRFAyIiwKICAiRVA0MDAiLCAiTVJHQlAiLCAiRU1TWSIsICJSWUJQIiwgIkhJUklQMyIsICJUT1BCUDEiLAogICJNR0EiLCAiUFJETTgiLCAiRVpIMiIsICJIREFDNSIsICJETUFQMSIsICJOUEFUIiwgIlNBUDEzMCIsCiAgIlNBTUQxIiwgIlpNWU5EOCIsICJBU0gxTCIsICJBUklEMUIiLCAiSERBQzQiLCAiQlJEOSIsICJOQ09SMSIsCiAgIk1DTTNBUCIsICJVVFAzIiwgIlpaRUYxIiwgIkROVFRJUDEiLCAiUFJLRDEiLCAiTVRBMSIsICJTQVRCMiIsCiAgIktBTlNMMyIsICJSRVNUIiwgIlNJUlQ2IiwgIlBIQzIiLCAiVE9QT1JTIiwgIkpNSkQxQyIsICJCUkQyIiwKICAiQVRGN0lQIiwgIlNQMSIsICJLQU5TTDEiLCAiQk1JMSIsICJDT01NRDMtQk1JMSIsICJLTVQyQyIsICJBU1hMMiIsCiAgIlRQNTNCUDEiLCAiWVkxQVAxIiwgIldBQyIsICJCQVAxIiwgIkwzTUJUTDIiLCAiWk5GNTE4QSIsICJNQkQzIiwKICAiT0JJMSIsICJET1QxTCIsICJLRE01QyIsICJSSU5HMSIsICJVU1AxNiIsICJDTk9UMyIsICJBVFhOMUwiLCAiUkNPUjMiLAogICJSQ09SMiIsICJTQ01MMiIsICJLTVQyRCIsICJBUklEMUEiLCAiR0FUQUQyQiIsICJBVE0iLCAiR1RGM0M0IiwgIlNQMTQwIiwKICAiQ0hEOCIsICJCUFRGIiwgIk1FQ1AyIiwgIkFIQ1RGMSIsICJLRE01QSIsICJNWVNNMSIsICJBVEY3SVAyIiwgIk5JUEJMIiwKICAiTFJXRDEiLCAiQVNIMkwiLCAiTUFSSzMiLCAiUlJQMUIiLCAiRVAzMDAiLCAiRUhNVDIiLCAiVE5SQzE4IiwgIk1EQzEiLAogICJSQUkxIiwgIkNJUjEiLCAiRk9YTzMiLCAiQVNYTDEiLCAiVUJFMk8iLCAiS0RNMkIiLCAiRFBGMiIsICJCUkQ0IiwKICAiTkFDQzEiLCAiQ0hENCIsICJUUklNMjgiLCAiUlVWQkwxIiwgIkFDVEw2QiIsICJEREIxIiwgIkFDVEw2QSIsCiAgIlNUSzQiLCAiQ0FSTTEiLCAiU1VNTzMiLCAiRU5ZMiIsICJDVUw1IiwgIlBIRjVBIiwgIlNJUlQyIiwgIlBLTjEiLAogICJNQVBLQVBLMiIsICJNQUdFQTEiLCAiWk5GNTE4QiIsICJTTUNIRDEiLCAiRExEIiwgIk1FTjEiLCAiSERBQzYiLAogICJEVFgzTCIsICJQU01BOCIsICJIMS02IiwgIlBNTCIsICJORUs2IiwgIlBIRjIwTDEiLCAiUkJCUDYiCikKR3JvdXAyX3Bob3NwaG8gPC0gYygKICAiRkFOQ0kiLCAiQ0RLMTQiLCAiUFJETTEiLCAiSUNBTTEiLCAiQ0RLMSIsICJSVU5YMyIsICJOT1RDSDMiLCAiQ0FTUDgiLAogICJCU1QyIiwgIkNUU0wiLCAiTVgyIiwgIkNBU1AxIiwgIlNUQVQyIiwgIk1YMSIKKQpgYGAKCmBgYHtyIHBob3NwaG8tY2F0ZWdvcmllc30KcGhvc3Bob19yYXckRGlmZkV4cCA8LSBjYXNlX3doZW4oCiAgYWJzKHBob3NwaG9fcmF3JGBsb2cyKEZDKWApID49IHBob3NwaG9fRkNfdGhyZXNob2xkICYKICAgIHBob3NwaG9fcmF3JGBQIHZhbHVlYCA+IHBob3NwaG9fcF90aHJlc2hvbGQgfiAiRkMgb25seSIsCiAgYWJzKHBob3NwaG9fcmF3JGBsb2cyKEZDKWApIDwgcGhvc3Bob19GQ190aHJlc2hvbGQgJgogICAgcGhvc3Bob19yYXckYFAgdmFsdWVgIDw9IHBob3NwaG9fcF90aHJlc2hvbGQgfiAiUCB2YWwuIG9ubHkiLAogIGFicyhwaG9zcGhvX3JhdyRgbG9nMihGQylgKSA+PSBwaG9zcGhvX0ZDX3RocmVzaG9sZCAmCiAgICBwaG9zcGhvX3JhdyRgUCB2YWx1ZWAgPD0gcGhvc3Bob19wX3RocmVzaG9sZCB+ICJGQyBhbmQgUCB2YWwuIiwKICAuZGVmYXVsdCA9ICJOUyIKKQpwaG9zcGhvX3JhdyREaWZmRXhwIDwtIGZhY3RvcihwaG9zcGhvX3JhdyREaWZmRXhwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJGQyBhbmQgUCB2YWwuIiwgIlAgdmFsLiBvbmx5IiwgIkZDIG9ubHkiLCAiTlMiKSkKCiMgQWRkaW5nIGluIGFuIGFscGhhIHZhbHVlIGR1ZSB0byBwb2ludCBzcHJlYWQgYW5kIGhpZ2hlciBoaXQgY291bnQKcGhvc3Bob19yYXckREVBbHBoYSA8LSAwLjMKcGhvc3Bob19yYXckREVBbHBoYVthYnMocGhvc3Bob19yYXckYGxvZzIoRkMpYCkgPj0gcGhvc3Bob19GQ190aHJlc2hvbGQgJgogICAgICAgICAgICAgICAgICAgcGhvc3Bob19yYXckYFAgdmFsdWVgIDw9IHBob3NwaG9fcF90aHJlc2hvbGRdIDwtIDAuOAoKCgojIFBpY2tpbmcgYSBtdWNoIHNtYWxsZXIgc3Vic2V0IG9mIHBvaW50cyBpbiBHcm91cCAxIHRvIGxhYmVsIGp1c3QgZm9yIGVhc2Ugb2Ygdmlld2luZwpHcm91cDFfcGhvc3Bob19zdWIgPC0gYygKICAiQVNIMkwiLCAiQVNYTDEiLCAiQVNYTDIiLCAiQkFQMSIsICJCTUkxIiwgIkNIRDQiLCAiRVpIMiIsICJETUFQMSIsCiAgIkVQNDAwIiwgIkdBVEFEMkIiLCAiS0FOU0wxIiwgIktBTlNMMyIsICJLTVQyQyIsICJLTVQyRCIsICJNQkQzIiwKICAiTUVOMSIsICJNR0EiLCAiTVJHQlAiLCAiTVRBMSIsICJOQ09BNiIsICJQSEMyIiwgIlBIRjIwTDEiLCAiUkVTVCIsCiAgIlJJTkcxIiwgIlJVVkJMMSIsICJTQVAxMzAiLCAiU0NNTDIiLCAiU0lSVDMiLCAiU0lSVDYiLCAiRVAzMDAiCikKcGhvc3Bob19yYXckbGFiZWxzIDwtIGlmX2Vsc2UoCiAgYWJzKHBob3NwaG9fcmF3JGBsb2cyKEZDKWApID49IHBob3NwaG9fRkNfdGhyZXNob2xkICYKICAgIHBob3NwaG9fcmF3JGBQIHZhbHVlYCA8PSBwaG9zcGhvX3BfdGhyZXNob2xkLCBwaG9zcGhvX3JhdyRTWU1CT0wyLCAiIikKYGBgCgpgYGB7ciBwaG9zcGhvLWdyb3VwZWQtdm9sY2Fuby1wbG90fQpnZ3Bsb3QocGhvc3Bob19yYXcsIGFlcyh4ID0gYGxvZzIoRkMpYCwgeSA9IC1sb2cxMChgUCB2YWx1ZWApKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gRGlmZkV4cCwgYWxwaGEgPSBERUFscGhhKSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMocGhvc3Bob19GQ190aHJlc2hvbGQsIC1waG9zcGhvX0ZDX3RocmVzaG9sZCksCiAgICAgICAgICAgICBhbHBoYSA9IDAuNSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMChwaG9zcGhvX3BfdGhyZXNob2xkKSwKICAgICAgICAgICAgIGFscGhhID0gMC41LCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IGxhYmVscyksIHNob3cubGVnZW5kID0gRkFMU0UsIG1heC5vdmVybGFwcyA9IDIwLAogICAgICAgICAgICAgICAgICBzaXplID0gcmVsKDMpKSArCiAgZ3VpZGVzKGFscGhhID0gIm5vbmUiKSArCiAgbGFicyh4ID0gImxvZzIoRm9sZCBDaGFuZ2UpIiwgeSA9ICItbG9nMTAoUCB2YWx1ZSkiLAogICAgICAgdGl0bGUgPSAiRmlndXJlIDIuMWE6IFZvbGNhbm8gcGxvdCIsCiAgICAgICBjb2xvciA9ICJDYXRlZ29yeSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKAogICAgYWVzdGhldGljcyA9ICJjb2xvciIsIG9wdGlvbiA9ICJtYWdtYSIsCiAgICBiZWdpbiA9IDAuMiwgZW5kID0gMC45CiAgKQpgYGAKCiMjIFBhdGh3YXkgZW5yaWNobWVudAoKYGBge3IgcGhvc3Boby1lbnJpY2h9CiMgU2VwYXJhdGUgcGhvc3Bob19zaWduaWZpY2FudCBzdWJzZXQgaW50byB1cHJlZ3VsYXRlZCB2cyBkb3ducmVndWxhdGVkCnBob3NwaG9fc2lnbmlmaWNhbnRfdXAgPC0gZmlsdGVyKHBob3NwaG9fc2lnbmlmaWNhbnQsIGBsb2cyKEZDKWAgPiAwKQpwaG9zcGhvX3NpZ25pZmljYW50X2Rvd24gPC0gZmlsdGVyKHBob3NwaG9fc2lnbmlmaWNhbnQsIGBsb2cyKEZDKWAgPCAwKQoKIyBFbnJpY2hpbmcgZm9yICp0b3RhbCogR08gdGVybXMgLSBvcHRpb25hbCwgY2FuIGJlIHVzZWQgdG8gdmlzdWFsaXplIG92ZXJhbGwgcmVzdWx0cwpwaG9zcGhvX0dPX3VwIDwtIGVucmljaEdPKHBob3NwaG9fc2lnbmlmaWNhbnRfdXAkRU5UUkVaSUQsICJvcmcuSHMuZWcuZGIiKQpwaG9zcGhvX0dPX2Rvd24gPC0gZW5yaWNoR08ocGhvc3Bob19zaWduaWZpY2FudF9kb3duJEVOVFJFWklELCAib3JnLkhzLmVnLmRiIikKCiMgRW5yaWNoIGZvciBLRUdHIHBhdGh3YXlzCnBob3NwaG9fS0VHR191cCA8LSBlbnJpY2hLRUdHKHBob3NwaG9fc2lnbmlmaWNhbnRfdXAkRU5UUkVaSUQpCnBob3NwaG9fS0VHR19kb3duIDwtIGVucmljaEtFR0cocGhvc3Bob19zaWduaWZpY2FudF9kb3duJEVOVFJFWklEKQoKIyBkb3RwbG90KCkgY2FuIGJlIHVzZWQgZm9yIGFuIG92ZXJ2aWV3IG9mIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4ga2V5IHRlcm1zCmRvdHBsb3QocGhvc3Bob19LRUdHX3VwLCBzaG93Q2F0ZWdvcnkgPSAyMCwgZm9udC5zaXplID0gcmVsKDEpLAogICAgICAgIHRpdGxlID0gIkZpZ3VyZSAyLjJhOiBQaG9zcGhvIEtFR0cgdXAiLCBsYWJlbF9mb3JtYXQgPSA1MCkgKwogIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpkb3RwbG90KHBob3NwaG9fS0VHR19kb3duLCBzaG93Q2F0ZWdvcnkgPSAyMCwgZm9udC5zaXplID0gcmVsKDEpLAogICAgICAgIHRpdGxlID0gIkZpZ3VyZSAyLjJiOiBQaG9zcGhvIEtFR0cgZG93biIsIGxhYmVsX2Zvcm1hdCA9IDUwKSArCiAgdGhlbWUocGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmRvdHBsb3QocGhvc3Bob19HT191cCwgc2hvd0NhdGVnb3J5ID0gMjAsIGZvbnQuc2l6ZSA9IHJlbCgxKSwKICAgICAgICB0aXRsZSA9ICJGaWd1cmUgMi4yYzogUGhvc3BobyBHTyB1cCIsIGxhYmVsX2Zvcm1hdCA9IDcwKSArCiAgdGhlbWUocGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmRvdHBsb3QocGhvc3Bob19HT19kb3duLCBzaG93Q2F0ZWdvcnkgPSAyMCwgZm9udC5zaXplID0gcmVsKDEpLAogICAgICAgIHRpdGxlID0gIkZpZ3VyZSAyLjJkOiBQaG9zcGhvIEdPIGRvd24iLCBsYWJlbF9mb3JtYXQgPSA1MCkgKwogIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpgYGAKCmBgYHtyIHBob3NwaG8tR08tdXAtc3BsaXR9CiMgU2VwYXJhdGluZyB0aGUgR08gdGVybXMgYnkgb250b2xvZ3kgYXNwZWN0CnBob3NwaG9fR09fdXBfTUYgPC0gZW5yaWNoR08ocGhvc3Bob19zaWduaWZpY2FudF91cCRFTlRSRVpJRCwKICAib3JnLkhzLmVnLmRiIiwgb250ID0gIk1GIgopCnBob3NwaG9fR09fdXBfQlAgPC0gZW5yaWNoR08ocGhvc3Bob19zaWduaWZpY2FudF91cCRFTlRSRVpJRCwKICAib3JnLkhzLmVnLmRiIiwgb250ID0gIkJQIgopCnBob3NwaG9fR09fdXBfQ0MgPC0gZW5yaWNoR08ocGhvc3Bob19zaWduaWZpY2FudF91cCRFTlRSRVpJRCwKICAib3JnLkhzLmVnLmRiIiwgb250ID0gIkNDIgopCmBgYAoKYGBge3IgcGhvc3Boby1HTy11cC1jbGVhbn0KIyBUYWtpbmcgdGhlIHRvcCAxMCBlbnJpY2hlZCB0ZXJtcyBpbiBlYWNoIGNhdGVnb3J5IGJ5IFAgdmFsdWUKcGhvc3Bob19HT191cF9zbGljZV9NRiA8LSBzbGljZV9taW4oCiAgZmlsdGVyKHBob3NwaG9fR09fdXBfTUZAcmVzdWx0LCBwLmFkanVzdCA8IDAuMDUpLAogIG9yZGVyX2J5ID0gYHAuYWRqdXN0YCwgbiA9IDEwCikKcGhvc3Bob19HT191cF9zbGljZV9CUCA8LSBzbGljZV9taW4oCiAgZmlsdGVyKHBob3NwaG9fR09fdXBfQlBAcmVzdWx0LCBwLmFkanVzdCA8IDAuMDUpLAogIG9yZGVyX2J5ID0gYHAuYWRqdXN0YCwgbiA9IDEwCikKcGhvc3Bob19HT191cF9zbGljZV9DQyA8LSBzbGljZV9taW4oCiAgZmlsdGVyKHBob3NwaG9fR09fdXBfQ0NAcmVzdWx0LCBwLmFkanVzdCA8IDAuMDUpLAogIG9yZGVyX2J5ID0gYHAuYWRqdXN0YCwgbiA9IDEwCikKCiMgQ29tYmluaW5nIHRvcCAxMCB0ZXJtcyBpbiBlYWNoIGFzcGVjdApwaG9zcGhvX0dPX3VwX3NsaWNlX0FMTCA8LSBiaW5kX3Jvd3MoCiAgIk1GIiA9IHBob3NwaG9fR09fdXBfc2xpY2VfTUYsCiAgIkJQIiA9IHBob3NwaG9fR09fdXBfc2xpY2VfQlAsCiAgIkNDIiA9IHBob3NwaG9fR09fdXBfc2xpY2VfQ0MsCiAgLmlkID0gIk9udG9sb2d5IgopCnBob3NwaG9fR09fdXBfc2xpY2VfQUxMJEdSIDwtIHBhcnNlX3JhdGlvKHBob3NwaG9fR09fdXBfc2xpY2VfQUxMJEdlbmVSYXRpbykKCiMgQWRkZWQgaW4gdG8gc2hvcnRlbiBuYW1lcyB3aGVuIHBsb3R0ZWQgLSB0aGlzIGNhbiBiZSByZW1vdmVkIG9yIGFkanVzdGVkCnBob3NwaG9fR09fdXBfc2xpY2VfQUxMWyJEZXNjcmlwdGlvbiJdW3Bob3NwaG9fR09fdXBfc2xpY2VfQUxMWyJEZXNjcmlwdGlvbiJdID09ICJveGlkb3JlZHVjdGFzZSBhY3Rpdml0eSwgYWN0aW5nIG9uIHRoZSBhbGRlaHlkZSBvciBveG8gZ3JvdXAgb2YgZG9ub3JzLCBOQUQgb3IgTkFEUCBhcyBhY2NlcHRvciJdIDwtICJveGlkb3JlZHVjdGFzZSwgYWxkZWh5ZGUvb3hvIGRvbm9yLCBOQUQvTkFEUCBhY2NlcHRvciIKCiMgUmVvcmRlcmluZyB0aGUgdG9wIHRlcm1zIGJ5IGdlbmUgcmF0aW8KcGhvc3Bob19HT191cF9zbGljZV9BTEwkRGVzY3JpcHRpb24gPC0gZmN0X3Jlb3JkZXIoCiAgcGhvc3Bob19HT191cF9zbGljZV9BTEwkRGVzY3JpcHRpb24sIHBob3NwaG9fR09fdXBfc2xpY2VfQUxMJEdSCikKYGBgCgpgYGB7ciBwaG9zcGhvLUtFR0ctdXAtZW5yaWNofQojIFJlcGVhdCBmb3IgS0VHRwojIFRha2luZyB0aGUgdG9wIDEwIGVucmljaGVkIHRlcm1zIGJ5IFAgdmFsdWUKcGhvc3Bob19LRUdHX3VwX3NsaWNlIDwtIHNsaWNlX21pbigKICBmaWx0ZXIocGhvc3Bob19LRUdHX3VwQHJlc3VsdCwgcC5hZGp1c3QgPCAwLjA1KSwKICBvcmRlcl9ieSA9IGBwLmFkanVzdGAsIG4gPSAxMAopCgojIFJlb3JkZXJpbmcgdGhlIHRvcCB0ZXJtcyBieSBnZW5lIHJhdGlvCnBob3NwaG9fS0VHR191cF9zbGljZSRHUiA8LSBwYXJzZV9yYXRpbyhwaG9zcGhvX0tFR0dfdXBfc2xpY2UkR2VuZVJhdGlvKQpwaG9zcGhvX0tFR0dfdXBfc2xpY2UkRGVzY3JpcHRpb24gPC0gZmN0X3Jlb3JkZXIoCiAgcGhvc3Bob19LRUdHX3VwX3NsaWNlJERlc2NyaXB0aW9uLAogIHBob3NwaG9fS0VHR191cF9zbGljZSRHUgopCnBob3NwaG9fS0VHR191cF9zbGljZSRPbnRvbG9neSA8LSAiS0VHRyIKYGBgCgpgYGB7ciBwaG9zcGhvLXVwLWNvbWJpbmV9CiMgQ29tYmluaW5nIEdPIGFuZCBLRUdHIGZvciB1cHJlZ3VsYXRlZCBwcm90ZWlucwpwaG9zcGhvX3VwX2JvdW5kIDwtIGJpbmRfcm93cygKICBwaG9zcGhvX0dPX3VwX3NsaWNlX0FMTCwgcGhvc3Bob19LRUdHX3VwX3NsaWNlCikKcGhvc3Bob191cF9ib3VuZCREZXNjcmlwdGlvbiA8LSBmY3RfcmVvcmRlcigKICBwaG9zcGhvX3VwX2JvdW5kJERlc2NyaXB0aW9uLCBwaG9zcGhvX3VwX2JvdW5kJEdSCikKCiMgTWFudWFsbHkgb3JkZXJpbmcgZmFjdG9ycyB0byBkaXNwbGF5IGFsbCBHTyBhc3BlY3RzIHRvZ2V0aGVyCnBob3NwaG9fdXBfYm91bmQkT250b2xvZ3kgPC0gZmFjdG9yKHBob3NwaG9fdXBfYm91bmQkT250b2xvZ3ksCiAgbGV2ZWxzID0gYygiQlAiLCAiQ0MiLCAiTUYiLCAiS0VHRyIpCikKYGBgCgpgYGB7ciBwaG9zcGhvLXVwLXBhdGh3YXlzLCBmaWcuYXNwID0gMC45fQojIEdyYXBoaW5nIGFsbCA0IGNhdGVnb3JpZXMgdG9nZXRoZXIKZ2dwbG90KAogIHBob3NwaG9fdXBfYm91bmQsCiAgIyBHZW5lIHJhdGlvIG9uIFggYXhpcywgdmVyYm9zZSBwYXRod2F5IGRlc2NyaXB0aW9uIG9uIFkgYXhpcwogICMgQ29sb3JpbmcgYnkgYWRqdXN0ZWQgUCB2YWx1ZSBhbmQgc2l6ZSBieSB0aGUgY291bnQgb2YgZ2VuZXMgaW4gYSBwYXRod2F5CiAgYWVzKHggPSBHUiwgeSA9IERlc2NyaXB0aW9uLCBjb2xvciA9IHAuYWRqdXN0LCBzaXplID0gQ291bnQpCiAgKSArCiAgc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHMgPSBsYWJlbF93cmFwKDgwKSkgKwogIGdlb21fY291bnQoKSArCiAgbGFicygKICAgIHggPSAiR2VuZSByYXRpbyIsIHkgPSAiUGF0aHdheSIsCiAgICB0aXRsZSA9ICJGaWd1cmUgMi4yZTogVXByZWd1bGF0ZWQgcGF0aHdheXMiCiAgKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKAogICAgYWVzdGhldGljcyA9ICJjb2xvciIsIG9wdGlvbiA9ICJtYWdtYSIsCiAgICBuYW1lID0gIkFkanVzdGVkIFAiLCBiZWdpbiA9IDAsIGVuZCA9IDAuOQogICkgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDI1LCAyNCkpICsKICBmYWNldF9ncmlkKE9udG9sb2d5IH4gLiwKICAgIGRyb3AgPSBUUlVFLCBzcGFjZSA9ICJmcmVlX3kiLAogICAgc2NhbGVzID0gImZyZWVfeSIKICApICsKICB0aGVtZSgKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgIHBhbmVsLnNwYWNpbmcgPSB1bml0KDAuNiwgImxpbmVzIiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDEuMSkpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJsaWdodCBncmF5IiksCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyYXk5MCIpCiAgKQpgYGAKCmBgYHtyIHBob3NwaG8tR08tZG93bi1zcGxpdH0KIyBTYW1lIHByb2Nlc3MgYXMgdGhlIHByZXZpb3VzIGNodW5rcwojIFNlcGFyYXRpbmcgdGhlIEdPIHRlcm1zIGJ5IG9udG9sb2d5IGFzcGVjdApwaG9zcGhvX0dPX2Rvd25fTUYgPC0gZW5yaWNoR08ocGhvc3Bob19zaWduaWZpY2FudF9kb3duJEVOVFJFWklELAogICJvcmcuSHMuZWcuZGIiLAogIG9udCA9ICJNRiIKKQpwaG9zcGhvX0dPX2Rvd25fQlAgPC0gZW5yaWNoR08ocGhvc3Bob19zaWduaWZpY2FudF9kb3duJEVOVFJFWklELAogICJvcmcuSHMuZWcuZGIiLAogIG9udCA9ICJCUCIKKQpwaG9zcGhvX0dPX2Rvd25fQ0MgPC0gZW5yaWNoR08ocGhvc3Bob19zaWduaWZpY2FudF9kb3duJEVOVFJFWklELAogICJvcmcuSHMuZWcuZGIiLAogIG9udCA9ICJDQyIKKQpgYGAKCmBgYHtyIHBob3NwaG8tR08tZG93bi1jbGVhbn0KIyBUYWtpbmcgdGhlIHRvcCAxMCBlbnJpY2hlZCB0ZXJtcyBpbiBlYWNoIGNhdGVnb3J5IGJ5IFAgdmFsdWUKcGhvc3Bob19HT19kb3duX3NsaWNlX01GIDwtIHNsaWNlX21pbigKICBmaWx0ZXIocGhvc3Bob19HT19kb3duX01GQHJlc3VsdCwgcC5hZGp1c3QgPCAwLjA1KSwKICBvcmRlcl9ieSA9IGBwLmFkanVzdGAsIG4gPSAxMAopCnBob3NwaG9fR09fZG93bl9zbGljZV9CUCA8LSBzbGljZV9taW4oCiAgZmlsdGVyKHBob3NwaG9fR09fZG93bl9CUEByZXN1bHQsIHAuYWRqdXN0IDwgMC4wNSksCiAgb3JkZXJfYnkgPSBgcC5hZGp1c3RgLCBuID0gMTAKKQpwaG9zcGhvX0dPX2Rvd25fc2xpY2VfQ0MgPC0gc2xpY2VfbWluKAogIGZpbHRlcihwaG9zcGhvX0dPX2Rvd25fQ0NAcmVzdWx0LCBwLmFkanVzdCA8IDAuMDUpLAogIG9yZGVyX2J5ID0gYHAuYWRqdXN0YCwgbiA9IDEwCikKCiMgQ29tYmluaW5nIHRvcCAxMCB0ZXJtcyBpbiBlYWNoIGFzcGVjdApwaG9zcGhvX2Rvd25fYm91bmQgPC0gYmluZF9yb3dzKAogICJNRiIgPSBwaG9zcGhvX0dPX2Rvd25fc2xpY2VfTUYsCiAgIkJQIiA9IHBob3NwaG9fR09fZG93bl9zbGljZV9CUCwKICAiQ0MiID0gcGhvc3Bob19HT19kb3duX3NsaWNlX0NDLAogIC5pZCA9ICJPbnRvbG9neSIKKQpgYGAKCmBgYHtyIHBob3NwaG8tS0VHRy1kb3duLWVucmljaH0KIyBSZXBlYXQgZm9yIEtFR0cKIyBUYWtpbmcgdGhlIHRvcCAxMCBlbnJpY2hlZCB0ZXJtcyBieSBQIHZhbHVlCnBob3NwaG9fS0VHR19kb3duX3NsaWNlIDwtIHNsaWNlX21pbigKICBmaWx0ZXIocGhvc3Bob19LRUdHX2Rvd25AcmVzdWx0LCBwLmFkanVzdCA8IDAuMDUpLAogIG9yZGVyX2J5ID0gYHAuYWRqdXN0YCwgbiA9IDEwCikKCiMgUmVvcmRlcmluZyB0aGUgdG9wIHRlcm1zIGJ5IGdlbmUgcmF0aW8KcGhvc3Bob19LRUdHX2Rvd25fc2xpY2UkR1IgPC0gcGFyc2VfcmF0aW8ocGhvc3Bob19LRUdHX2Rvd25fc2xpY2UkR2VuZVJhdGlvKQpwaG9zcGhvX0tFR0dfZG93bl9zbGljZSREZXNjcmlwdGlvbiA8LSBmY3RfcmVvcmRlcigKICBwaG9zcGhvX0tFR0dfZG93bl9zbGljZSREZXNjcmlwdGlvbiwKICBwaG9zcGhvX0tFR0dfZG93bl9zbGljZSRHUgopCnBob3NwaG9fS0VHR19kb3duX3NsaWNlJE9udG9sb2d5IDwtICJLRUdHIgpgYGAKCmBgYHtyIHBob3NwaG8tZG93bi1jb21iaW5lfQojIENvbWJpbmluZyBHTyBhbmQgS0VHRyBmb3IgZG93bnJlZ3VsYXRlZCBwcm90ZWlucwpwaG9zcGhvX2Rvd25fYm91bmQgPC0gYmluZF9yb3dzKAogIHBob3NwaG9fZG93bl9ib3VuZCwgcGhvc3Bob19LRUdHX2Rvd25fc2xpY2UKKQpwaG9zcGhvX2Rvd25fYm91bmQkR1IgPC0gcGFyc2VfcmF0aW8ocGhvc3Bob19kb3duX2JvdW5kJEdlbmVSYXRpbykKCnBob3NwaG9fZG93bl9ib3VuZCREZXNjcmlwdGlvbiA8LSBmY3RfcmVvcmRlcigKICBwaG9zcGhvX2Rvd25fYm91bmQkRGVzY3JpcHRpb24sIHBob3NwaG9fZG93bl9ib3VuZCRHUgopCgojIE1hbnVhbGx5IG9yZGVyaW5nIGZhY3RvcnMgdG8gZGlzcGxheSBhbGwgR08gYXNwZWN0cyB0b2dldGhlcgpwaG9zcGhvX2Rvd25fYm91bmQkT250b2xvZ3kgPC0gZmFjdG9yKHBob3NwaG9fZG93bl9ib3VuZCRPbnRvbG9neSwKICBsZXZlbHMgPSBjKCJCUCIsICJDQyIsICJNRiIsICJLRUdHIikKKQpgYGAKCmBgYHtyIHBob3NwaG8tZG93bi1wYXRod2F5cywgZmlnLmFzcCA9IDAuOX0KIyBHcmFwaGluZyBhbGwgNCBjYXRlZ29yaWVzIHRvZ2V0aGVyCmdncGxvdCgKICBwaG9zcGhvX2Rvd25fYm91bmQsCiAgIyBHZW5lIHJhdGlvIG9uIFggYXhpcywgdmVyYm9zZSBwYXRod2F5IGRlc2NyaXB0aW9uIG9uIFkgYXhpcwogICMgQ29sb3JpbmcgYnkgYWRqdXN0ZWQgUCB2YWx1ZSBhbmQgc2l6ZSBieSB0aGUgY291bnQgb2YgZ2VuZXMgaW4gYSBwYXRod2F5CiAgYWVzKHggPSBHUiwgeSA9IERlc2NyaXB0aW9uLCBjb2xvciA9IHAuYWRqdXN0LCBzaXplID0gQ291bnQpCiAgKSArCiAgc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHMgPSBsYWJlbF93cmFwKDcwKSkgKwogIGdlb21fY291bnQoKSArCiAgbGFicygKICAgIHggPSAiR2VuZSByYXRpbyIsIHkgPSAiUGF0aHdheSIsCiAgICB0aXRsZSA9ICJGaWd1cmUgMi4yZjogRG93bnJlZ3VsYXRlZCBwYXRod2F5cyIKICApICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoCiAgICBhZXN0aGV0aWNzID0gImNvbG9yIiwgb3B0aW9uID0gIm1hZ21hIiwKICAgIG5hbWUgPSAiQWRqdXN0ZWQgUCIsIGJlZ2luID0gMCwgZW5kID0gMC45CiAgKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMjUsIDI0KSkgKwogIGZhY2V0X2dyaWQoT250b2xvZ3kgfiAuLAogICAgZHJvcCA9IFRSVUUsIHNwYWNlID0gImZyZWVfeSIsCiAgICBzY2FsZXMgPSAiZnJlZV95IgogICkgKwogIHRoZW1lKAogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgcGFuZWwuc3BhY2luZyA9IHVuaXQoMC42LCAibGluZXMiKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMS4xKSksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gImxpZ2h0IGdyYXkiKSwKICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTkwIikKICApCmBgYAoKYGBge3IgZW1hcC10b3RhbCwgZmlnLmFzcCA9IDF9CnBob3NwaG9fR09fdXBfTUYgPC0gcGFpcndpc2VfdGVybXNpbShwaG9zcGhvX0dPX3VwX01GKQplbWFwcGxvdChwaG9zcGhvX0dPX3VwX01GLCBsYXlvdXQgPSAibmljZWx5Iiwgbm9kZV9sYWJlbCA9ICJub25lIikgKwogIGdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBzdHJfd3JhcChuYW1lLCB3aWR0aCA9IDYwKSkpICsKICBnZ3RpdGxlKCJGaWd1cmUgMi4yZzogVXByZWd1bGF0ZWQgZW5yaWNobWVudCBwbG90IChwaG9zcGhvLCBNRikiKSArCiAgdGhlbWUocGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMSwgMSwgMSwgMSkpCgpwaG9zcGhvX0dPX2Rvd25fTUYgPC0gcGFpcndpc2VfdGVybXNpbShwaG9zcGhvX0dPX2Rvd25fTUYpCmVtYXBwbG90KHBob3NwaG9fR09fZG93bl9NRiwgbGF5b3V0ID0gIm5pY2VseSIsIG5vZGVfbGFiZWwgPSAibm9uZSIpICsKICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gc3RyX3dyYXAobmFtZSwgd2lkdGggPSA2MCkpKSArCiAgZ2d0aXRsZSgiRmlndXJlIDIuMmg6IERvd25yZWd1bGF0ZWQgZW5yaWNobWVudCBwbG90IChwaG9zcGhvLCBNRikiKSArCiAgdGhlbWUocGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMSwgMSwgMSwgMSkpCmBgYAoKIyBIaXN0b25lIHByb2Nlc3NpbmcKClRoaXMgc2VjdGlvbiBwcm9jZXNzZXMgZGF0YSBvdXRwdXQgZnJvbSBFcGlQcm9maWxlLiBXZSBjaG9vc2UgdG8gYXNzZXNzIGJvdGggdGFpbCBmcmFnbWVudHMgYW5kIGluZGl2aWR1YWwgc3VtbWVkIFBUTXMgdG8gaW5kaWNhdGUgY28tb2NjdXJyZW5jZSwgd2hpY2ggbWF5IHNpZ25pZnkgYWN0aXZpdHkgb2YgY2VydGFpbiBlcGlnZW5ldGljIGFjdG9ycy4gTm90ZSB0aGF0IHRoZSBleHBvcnRlZCBgLnhsc2AgZmlsZXMgd2lsbCBoYXZlIHRvIGJlIG9wZW5lZCBtYW51YWxseSBvbmUgdGltZSwgdGhlbiBzYXZlZCBhZ2FpbiBhcyBgLnhscyh4KWAgZm9ybWF0IGR1ZSB0byB0aGUgb3V0cHV0IGZvcm1hdC4gSW50byB0aGUgYHJhbmdlYCBhcmd1bWVudCwgcGFzcyB0aGUgY2VsbCBsb2NhdGlvbiBvZiB0aGUgZmlyc3QgcGVwdGlkZSBuYW1lICgiSDNfM184IHVubW9kIiBpbiBvdXIgZGF0YSkgdG8gc2V0IHRoZSB0b3AgbGVmdCBjb3JuZXIsIHRoZW4gKE5BLCBbdGFyZ2V0IGNvbF0pIGFzIHRoZSBzZWNvbmQgbG9jYXRpb24gdG8gc2V0IHRoZSBib3R0b20gcmlnaHQgY29ybmVyLiBUaGlzIHNlY3Rpb24gYXNzdW1lcyB0aGUgZGF0YSBpcyBzZXBhcmF0aW5nIHR3byBjb25kaXRpb25zLCBpbmZlY3RlZCBhbmQgY29udHJvbC4KCmBgYHtyIGhpc3RvbmUtaW1wb3J0fQpoaXN0b25lX3JhdGlvc19wYXRoIDwtICJ+L0RvY3VtZW50cy9SL2hpc3RvbmVfcmF0aW9zL2hpc3RvbmVfcmF0aW9zLnhscyIKaGlzdG9uZV9zaW5nbGVfcGF0aCA8LSAifi9Eb2N1bWVudHMvUi9oaXN0b25lX3JhdGlvcy9oaXN0b25lX3JhdGlvc19zaW5nbGVfUFRNcy54bHMiCgojIFRoZSBjb2x1bW5zIHdpbGwgaGF2ZSB0byBiZSBtYW51YWxseSBwYXJzZWQgLSBpbmNsdWRlIHRoZSBuYW1lcyAoZmlyc3QgY29sdW1uKSBhbmQgcmF0aW9zIGZvciBhbGwgc2FtcGxlcwpoaXN0b25lX2ZyYWdfcmF3IDwtIGRyb3BfbmEocmVhZF9leGNlbChwYXRoID0gaGlzdG9uZV9yYXRpb3NfcGF0aCwgcmFuZ2UgPSBjZWxsX2xpbWl0cyhjKDQsIDEpLCBjKE5BLCAxMSkpLCBjb2xfbmFtZXMgPSBjKCJQVE0iLCAiQzEiLCAiSTEiLCAiQzIiLCAiSTIiLCAiQzMiLCAiSTMiLCAiQzQiLCAiSTQiLCAiQzUiLCAiSTUiKSkpCgpoaXN0b25lX3NpbmdsZV9yYXcgPC0gcmVhZF9leGNlbChwYXRoID0gaGlzdG9uZV9zaW5nbGVfcGF0aCwgY29sX25hbWVzID0gYygiUFRNIiwgIkMxIiwgIkkxIiwgIkMyIiwgIkkyIiwgIkMzIiwgIkkzIiwgIkM0IiwgIkk0IiwgIkM1IiwgIkk1IiksIHJhbmdlID0gY2VsbF9saW1pdHMoYygyLCAxKSwgYyhOQSwgMTEpKSkKCiMgU2V0IHRocmVzaG9sZCB2YWx1ZXMgZm9yIHByb3Rlb21lIGFuYWx5c2lzCmhpc3RvbmVfcF90aHJlc2hvbGQgPC0gMC4wNQpoaXN0b25lX0ZDX3RocmVzaG9sZCA8LSAwLjUKYGBgCgojIyBGcmFnbWVudHMKCk5hbWUgY2xlYW51cCBpcyBub3Qgc3RyaWN0bHkgbmVjZXNzYXJ5IGJ1dCBtYWtlcyBsYWJlbHMgZWFzaWVyIHRvIHJlYWQuIFRoZXJlIG1heSBhbHNvIGJlIHNvbWUgbG9uZyBhbWlubyBhY2lkIHN0cmluZ3Mgc3VjaCBhcyAiSDEgMS0zNSBIMTIuU0VUQVBBQVBBQUFQUEFFS0FQVktLS0FBS0tBR0dUUFIiIHRoYXQgaW5kaWNhdGUgYSB1bmlxdWUgaXNvZm9ybSAoSDEuMiBpbiB0aGlzIGNhc2UpLiBUaGVzZSBjYW4gYWxzbyBiZSBtb2RpZmllZCBieSBjYWxsaW5nIGBzdHJfcmVwbGFjZV9hbGxgIGFzIGRlc2lyZWQuCgpgYGB7ciBmcmFnbWVudC1oaXN0b25lLXByb2Nlc3Npbmd9CiMgQ2xlYW5pbmcgdXAgbmFtZXMKcmF0aW9zX2ZyYWcgPC0gaGlzdG9uZV9mcmFnX3JhdwpyYXRpb3NfZnJhZyRQVE0gPC0gcmF0aW9zX2ZyYWckUFRNICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgzXzNfOCIsICJIMyAzLTgiKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJIM185XzE3IiwgIkgzIDktMTciKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJIM18xOF8yNiIsICJIMyAxOC0yNiIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgzXzI3XzQwIiwgIkgzIDI3LTQwIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDMzXzI3XzQwIiwgIkgzLjMgMjctNDAiKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJIM180MV80OSIsICJIMyA0MS00OSIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgzXzU0XzYzIiwgIkgzIDU0LTYzIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDNfNzNfODMiLCAiSDMgNzMtODMiKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJIM18xMTdfMTI4IiwgIkgzIDExNy0xMjgiKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJINF80XzE3IiwgIkg0IDQtMTciKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJINF8yMF8yMyIsICJINCAyMC0yMyIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkg0XzI0XzM1IiwgIkg0IDI0LTM1IikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDRfNDBfNDUiLCAiSDQgNDAtNDUiKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJINF83OV85MiIsICJINCA3OS05MiIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgxXzFfMzUiLCAiSDEgMS0zNSIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgxXzU0XzgxIiwgIkgxIDU0LTcxIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBMV8zNl80MiIsICJIMkExIDM2LTQyIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBM18zNl80MiIsICJIMkEzIDM2LTQyIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBWF8zNl80MiIsICJIMkFYIDM2LTQyIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBMV80XzExIiwgIkgyQTEgNC0xMSIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgyQUpfNF8xMSIsICJIMkFKIDQtMTEiKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJIMkFYXzRfMTEiLCAiSDJBWCA0LTExIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBMV8xXzExIiwgIkgyQTEgMS0xMSIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgyQVZfMV8xOSIsICJIMkFWIDEtMTkiKSAlPiUgCiAgc3RyX3JlcGxhY2VfYWxsKCJIMkFaXzFfMTkiLCAiSDJBWiAxLTE5IikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBMV8xMl8xNyIsICJIMkExIDEyLTE3IikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBM18xMl8xNyIsICJIMkEzIDEyLTE3IikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBMV83Ml83NyIsICJIMkExIDcyLTc3IikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJBXzgyXzg4IiwgIkgyQSA4Mi04OCIpICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgyQV8xXzg4IiwgIkgyQSAxLTg4IikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDJCXzFfMjkiLCAiSDJCIDEtMjkiKQoKIyBHcm91cCB0d28gY29uZGl0aW9ucyB0b2dldGhlcgpyYXRpb3NfZnJhZyA8LSByYXRpb3NfZnJhZyAlPiUKICByZWxvY2F0ZShjb250YWlucygiSSIpLCAuYWZ0ZXIgPSBsYXN0X2NvbCgpKSAlPiUgCiAgcmVsb2NhdGUoIlBUTSIpCgojIENhbGN1bGF0ZSBpbmZlY3RlZC9jb250cm9sIGZvbGQgY2hhbmdlIGFuZCBQIHZhbHVlCnJhdGlvc19mcmFnIDwtIHJhdGlvc19mcmFnICU+JSAKICByb3d3aXNlKCkgJT4lIAogIG11dGF0ZShjX21lYW4gPSBtZWFuKGNfYWNyb3NzKCJDMSI6IkM1IikpLAogICAgICAgICBpX21lYW4gPSBtZWFuKGNfYWNyb3NzKCJJMSI6Ikk1IikpLAogICAgICAgICBGQyA9IGNfYWNyb3NzKGlfbWVhbikvY19hY3Jvc3MoY19tZWFuKSwKICAgICAgICAgImxvZzIoRkMpIiA9IGxvZzIoRkMpLAogICAgICAgICBpc19jb25zdGFudCA9IGFsbChjX2Fjcm9zcygiQzEiOiJDNSIpID09IGNfYWNyb3NzKCJJMSI6Ikk1IikpKQoKcmF0aW9zX2ZyYWcgPC0gY29sdW1uX3RvX3Jvd25hbWVzKHJhdGlvc19mcmFnLCB2YXIgPSAiUFRNIikKcmF0aW9zX2ZyYWckUFRNIDwtIHJvd25hbWVzKHJhdGlvc19mcmFnKQoKcmF0aW9zX2ZyYWckIlAgdmFsdWUiIDwtIE5BCnJhdGlvc19wIDwtIHJhdGlvc19mcmFnICU+JSAKICByb3d3aXNlKCkgJT4lIAogIGZpbHRlcihpc19jb25zdGFudCA9PSBGQUxTRSkgJT4lIAogICMgUmVtb3ZpbmcgYW55IHByb3RlaW5zIHRoYXQgaGF2ZSBhbGwgZXF1YWwgdmFsdWVzLCB3aGljaCBnaXZlcyBhbiAKICAjIGVycm9yIHdoZW4gcnVubmluZyB0LnRlc3QoKQogIG11dGF0ZSgiUCB2YWx1ZSIgPSB0LnRlc3QoY19hY3Jvc3MoIkMxIjoiQzUiKSwgY19hY3Jvc3MoIkkxIjoiSTUiKSwKICAgICAgICAgYWx0ZXJuYXRpdmUgPSAidHdvLnNpZGVkIiwgdmFyLmVxdWFsID0gVFJVRSkkcC52YWx1ZSkKCnJhdGlvc19mcmFnIDwtIGZ1bGxfam9pbihyYXRpb3NfZnJhZywgcmF0aW9zX3ApCgpyYXRpb3NfZnJhZyRgbG9nMihGQylgW3JhdGlvc19mcmFnJGBsb2cyKEZDKWAgPT0gIk5hTiJdIDwtIE5BCnJhdGlvc19mcmFnJEZDW3JhdGlvc19mcmFnJEZDID09ICJOYU4iXSA8LSBOQQpgYGAKClRvIGFkZHJlc3MgaW5maW5pdGUgZm9sZCBjaGFuZ2UgdmFsdWVzIHRoYXQgYXBwZWFyIHdoZW4gYSBwcm90ZWluIGlzIG5vdCBkZXRlY3RlZCBpbiBvbmUgY29uZGl0aW9uLCB3ZSBzZXQgdGhlbSB0byBoYXZlIGEgdW5pcXVlIHNoYXBlIGluIHRoZSBmaW5hbCBwbG90IGFuZCBhc3NpZ24gYSBmb2xkIGNoYW5nZSB2YWx1ZSBlcXVhbCB0byAxLjUgdGltZXMgdGhlIG1heGltdW0gdmFsaWQgdmFsdWUuCgpgYGB7ciBmcmFnbWVudC1zZXR1cH0KIyBHZW5lcmF0aW5nIG92ZXJhbGwgdm9sY2FubyBwbG90CiMgU2V0dGluZyBjYXRlZ29yaWVzIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbjoKIyBub3Qgc2lnbmlmaWNhbnQsIFAtdmFsdWUgb25seSwgRkMgb25seSwgYm90aApyYXRpb3NfZnJhZyREaWZmRXhwIDwtIGNhc2Vfd2hlbigKICBhYnMocmF0aW9zX2ZyYWckYGxvZzIoRkMpYCkgPj0gaGlzdG9uZV9GQ190aHJlc2hvbGQgJgogICAgcmF0aW9zX2ZyYWckYFAgdmFsdWVgID4gaGlzdG9uZV9wX3RocmVzaG9sZCB+ICJGQyBvbmx5IiwKICBhYnMocmF0aW9zX2ZyYWckYGxvZzIoRkMpYCkgPCBoaXN0b25lX0ZDX3RocmVzaG9sZCAmCiAgICByYXRpb3NfZnJhZyRgUCB2YWx1ZWAgPD0gaGlzdG9uZV9wX3RocmVzaG9sZCB+ICJQIHZhbC4gb25seSIsCiAgYWJzKHJhdGlvc19mcmFnJGBsb2cyKEZDKWApID49IGhpc3RvbmVfRkNfdGhyZXNob2xkICYKICAgIHJhdGlvc19mcmFnJGBQIHZhbHVlYCA8PSBoaXN0b25lX3BfdGhyZXNob2xkIH4gIkZDIGFuZCBQIHZhbC4iLAogIC5kZWZhdWx0ID0gIk5TIgopCnJhdGlvc19mcmFnJERpZmZFeHAgPC0gZmFjdG9yKHJhdGlvc19mcmFnJERpZmZFeHAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkZDIGFuZCBQIHZhbC4iLCAiUCB2YWwuIG9ubHkiLCAiRkMgb25seSIsICJOUyIpKQoKcmF0aW9zX2ZyYWckbGFiZWxzIDwtIGlmX2Vsc2UoCiAgYWJzKHJhdGlvc19mcmFnJGBsb2cyKEZDKWApID49IGhpc3RvbmVfRkNfdGhyZXNob2xkICYgCiAgICByYXRpb3NfZnJhZyRgUCB2YWx1ZWAgPD0gaGlzdG9uZV9wX3RocmVzaG9sZCwgcmF0aW9zX2ZyYWckUFRNLCAiIikKCnJhdGlvc19mcmFnX3Bsb3QgPC0gcmF0aW9zX2ZyYWcgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgZmlsdGVyKGBsb2cyKEZDKWAgIT0gIi1JbmYiICYgYGxvZzIoRkMpYCAhPSAiSW5mIikKcmF0aW9zX2ZyYWdfcGxvdCRzaGFwZSA8LSAiMTYiCgpyYXRpb3NfZnJhZ19pbnZhbGlkIDwtIHJhdGlvc19mcmFnICU+JSAKICBkcm9wX25hKCkgJT4lIAogIGZpbHRlcihgbG9nMihGQylgID09ICItSW5mIiB8IGBsb2cyKEZDKWAgPT0gIkluZiIpCnJhdGlvc19mcmFnX2ludmFsaWQkYGxvZzIoRkMpYFtyYXRpb3NfZnJhZ19pbnZhbGlkJGBsb2cyKEZDKWAgPT0gIi1JbmYiXSA8LSBtaW4ocmF0aW9zX2ZyYWdfcGxvdCRgbG9nMihGQylgKSoxLjUKcmF0aW9zX2ZyYWdfaW52YWxpZCRgbG9nMihGQylgW3JhdGlvc19mcmFnX2ludmFsaWQkYGxvZzIoRkMpYCA9PSAiSW5mIl0gPC0gbWF4KHJhdGlvc19mcmFnX3Bsb3QkYGxvZzIoRkMpYCkqMS41CnJhdGlvc19mcmFnX2ludmFsaWQkc2hhcGUgPC0gIjE3IgoKcmF0aW9zX2ZyYWdfcGxvdCA8LSBmdWxsX2pvaW4ocmF0aW9zX2ZyYWdfcGxvdCwgcmF0aW9zX2ZyYWdfaW52YWxpZCkKYGBgCgpgYGB7ciBmcmFnbWVudC12b2xjYW5vLXBsb3R9CmdncGxvdChyYXRpb3NfZnJhZ19wbG90LCBhZXMoeCA9IGBsb2cyKEZDKWAsIHkgPSAtbG9nMTAoYFAgdmFsdWVgKSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IERpZmZFeHAsIHNoYXBlID0gc2hhcGUpKSArCiAgZXhwYW5kX2xpbWl0cyh4ID0gYyhtaW4ocmF0aW9zX2ZyYWdfcGxvdCRgbG9nMihGQylgKSwgbWF4KHJhdGlvc19mcmFnX3Bsb3QkYGxvZzIoRkMpYCkpKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYyhoaXN0b25lX0ZDX3RocmVzaG9sZCwgLWhpc3RvbmVfRkNfdGhyZXNob2xkKSwKICAgICAgICAgICAgIGFscGhhID0gMC41LCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKGhpc3RvbmVfcF90aHJlc2hvbGQpLAogICAgICAgICAgICAgYWxwaGEgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gbGFiZWxzKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgbWF4Lm92ZXJsYXBzID0gMjAsCiAgICAgICAgICAgICAgICAgIHNpemUgPSByZWwoMykpICsKICBndWlkZXMoc2hhcGUgPSAibm9uZSIpICsKICBsYWJzKHggPSAibG9nMihGb2xkIENoYW5nZSkiLCB5ID0gIi1sb2cxMChQIHZhbHVlKSIsCiAgICAgICB0aXRsZSA9ICJGaWd1cmUgMy4xYTogRnJhZ21lbnQgdm9sY2FubyBwbG90IiwKICAgICAgIGNvbG9yID0gIkNhdGVnb3J5IikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoCiAgICBhZXN0aGV0aWNzID0gImNvbG9yIiwgb3B0aW9uID0gIm1hZ21hIiwKICAgIGJlZ2luID0gMC4yLCBlbmQgPSAwLjkpCmBgYAoKIyMgU2luZ2xlCgpgYGB7ciBzaW5nbGUtaGlzdG9uZS1wcm9jZXNzaW5nfQojIENsZWFuaW5nIHVwIG5hbWVzCnJhdGlvc19zaW5nbGUgPC0gaGlzdG9uZV9zaW5nbGVfcmF3CnJhdGlvc19zaW5nbGUkUFRNIDwtIHJhdGlvc19zaW5nbGUkUFRNICU+JSAKICBzdHJfcmVwbGFjZV9hbGwoIkgzMSIsICJIMy4xIikgJT4lIAogIHN0cl9yZXBsYWNlX2FsbCgiSDMzIiwgIkgzLjMiKQoKIyBHcm91cCB0d28gY29uZGl0aW9ucyB0b2dldGhlcgpyYXRpb3Nfc2luZ2xlIDwtIHJhdGlvc19zaW5nbGUgJT4lCiAgcmVsb2NhdGUoY29udGFpbnMoIkkiKSwgLmFmdGVyID0gbGFzdF9jb2woKSkgJT4lIAogIHJlbG9jYXRlKCJQVE0iKQoKIyBDYWxjdWxhdGUgaW5mZWN0ZWQvY29udHJvbCBmb2xkIGNoYW5nZSBhbmQgUCB2YWx1ZQpyYXRpb3Nfc2luZ2xlIDwtIHJhdGlvc19zaW5nbGUgJT4lIAogIHJvd3dpc2UoKSAlPiUgCiAgbXV0YXRlKGNfbWVhbiA9IG1lYW4oY19hY3Jvc3MoIkMxIjoiQzUiKSksCiAgICAgICAgIGlfbWVhbiA9IG1lYW4oY19hY3Jvc3MoIkkxIjoiSTUiKSksCiAgICAgICAgIEZDID0gY19hY3Jvc3MoaV9tZWFuKS9jX2Fjcm9zcyhjX21lYW4pLAogICAgICAgICAibG9nMihGQykiID0gbG9nMihGQyksCiAgICAgICAgICJQIHZhbHVlIiA9IHQudGVzdChjX2Fjcm9zcygiQzEiOiJDNSIpLCBjX2Fjcm9zcygiSTEiOiJJNSIpLAogICAgICAgICAgICAgICAgICAgICAgICBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiLCB2YXIuZXF1YWwgPSBUUlVFKSRwLnZhbHVlKQpyYXRpb3Nfc2luZ2xlIDwtIGNvbHVtbl90b19yb3duYW1lcyhyYXRpb3Nfc2luZ2xlLCB2YXIgPSAiUFRNIikKcmF0aW9zX3NpbmdsZSRQVE0gPC0gcm93bmFtZXMocmF0aW9zX3NpbmdsZSkKCnJhdGlvc19zaW5nbGUgPC0gcmF0aW9zX3NpbmdsZSAlPiUgCiAgcm93d2lzZSgpICU+JSAKICBtdXRhdGUoY19tZWFuID0gbWVhbihjX2Fjcm9zcygiQzEiOiJDNSIpKSwKICAgICAgICAgaV9tZWFuID0gbWVhbihjX2Fjcm9zcygiSTEiOiJJNSIpKSwKICAgICAgICAgRkMgPSBjX2Fjcm9zcyhpX21lYW4pL2NfYWNyb3NzKGNfbWVhbiksCiAgICAgICAgICJsb2cyKEZDKSIgPSBsb2cyKEZDKSwKICAgICAgICAgaXNfY29uc3RhbnQgPSBhbGwoY19hY3Jvc3MoIkMxIjoiQzUiKSA9PSBjX2Fjcm9zcygiSTEiOiJJNSIpKSkKCnJhdGlvc19zaW5nbGUgPC0gY29sdW1uX3RvX3Jvd25hbWVzKHJhdGlvc19zaW5nbGUsIHZhciA9ICJQVE0iKQpyYXRpb3Nfc2luZ2xlJFBUTSA8LSByb3duYW1lcyhyYXRpb3Nfc2luZ2xlKQoKcmF0aW9zX3NpbmdsZSQiUCB2YWx1ZSIgPC0gTkEKc2luZ2xlX3AgPC0gcmF0aW9zX3NpbmdsZSAlPiUgCiAgcm93d2lzZSgpICU+JSAKICBmaWx0ZXIoaXNfY29uc3RhbnQgPT0gRkFMU0UpICU+JSAKICAjIFJlbW92aW5nIGFueSBwcm90ZWlucyB0aGF0IGhhdmUgYWxsIGVxdWFsIHZhbHVlcywgd2hpY2ggZ2l2ZXMgYW4gCiAgIyBlcnJvciB3aGVuIHJ1bm5pbmcgdC50ZXN0KCkKICBtdXRhdGUoIlAgdmFsdWUiID0gdC50ZXN0KGNfYWNyb3NzKCJDMSI6IkM1IiksIGNfYWNyb3NzKCJJMSI6Ikk1IiksCiAgICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIsIHZhci5lcXVhbCA9IFRSVUUpJHAudmFsdWUpCgpyYXRpb3Nfc2luZ2xlIDwtIGZ1bGxfam9pbihyYXRpb3Nfc2luZ2xlLCBzaW5nbGVfcCkKCnJhdGlvc19zaW5nbGUkYGxvZzIoRkMpYFtyYXRpb3Nfc2luZ2xlJGBsb2cyKEZDKWAgPT0gIk5hTiJdIDwtIE5BCnJhdGlvc19zaW5nbGUkRkNbcmF0aW9zX3NpbmdsZSRGQyA9PSAiTmFOIl0gPC0gTkEKYGBgCgpgYGB7ciBzaW5nbGUtc2V0dXB9CiMgR2VuZXJhdGluZyBvdmVyYWxsIHZvbGNhbm8gcGxvdCAtIHNhbWUgYXMgcHJldmlvdXMKIyBTZXR0aW5nIGNhdGVnb3JpZXMgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uOgojIG5vdCBzaWduaWZpY2FudCwgUC12YWx1ZSBvbmx5LCBGQyBvbmx5LCBib3RoCnJhdGlvc19zaW5nbGUkRGlmZkV4cCA8LSBjYXNlX3doZW4oCiAgYWJzKHJhdGlvc19zaW5nbGUkYGxvZzIoRkMpYCkgPj0gaGlzdG9uZV9GQ190aHJlc2hvbGQgJgogICAgcmF0aW9zX3NpbmdsZSRgUCB2YWx1ZWAgPiBoaXN0b25lX3BfdGhyZXNob2xkIH4gIkZDIG9ubHkiLAogIGFicyhyYXRpb3Nfc2luZ2xlJGBsb2cyKEZDKWApIDwgaGlzdG9uZV9GQ190aHJlc2hvbGQgJgogICAgcmF0aW9zX3NpbmdsZSRgUCB2YWx1ZWAgPD0gaGlzdG9uZV9wX3RocmVzaG9sZCB+ICJQIHZhbC4gb25seSIsCiAgYWJzKHJhdGlvc19zaW5nbGUkYGxvZzIoRkMpYCkgPj0gaGlzdG9uZV9GQ190aHJlc2hvbGQgJgogICAgcmF0aW9zX3NpbmdsZSRgUCB2YWx1ZWAgPD0gaGlzdG9uZV9wX3RocmVzaG9sZCB+ICJGQyBhbmQgUCB2YWwuIiwKICAuZGVmYXVsdCA9ICJOUyIKKQpyYXRpb3Nfc2luZ2xlJERpZmZFeHAgPC0gZmFjdG9yKHJhdGlvc19zaW5nbGUkRGlmZkV4cCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiRkMgYW5kIFAgdmFsLiIsICJQIHZhbC4gb25seSIsICJGQyBvbmx5IiwgIk5TIikpCgojIFNldHRpbmcgbGFiZWxzCnJhdGlvc19zaW5nbGUkbGFiZWxzIDwtIGlmX2Vsc2UoCiAgYWJzKHJhdGlvc19zaW5nbGUkYGxvZzIoRkMpYCkgPj0gaGlzdG9uZV9GQ190aHJlc2hvbGQgJgogICAgcmF0aW9zX3NpbmdsZSRgUCB2YWx1ZWAgPD0gaGlzdG9uZV9wX3RocmVzaG9sZCwgcmF0aW9zX3NpbmdsZSRQVE0sICIiKQoKcmF0aW9zX3NpbmdsZV9wbG90IDwtIHJhdGlvc19zaW5nbGUgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgZmlsdGVyKGBsb2cyKEZDKWAgIT0gIi1JbmYiICYgYGxvZzIoRkMpYCAhPSAiSW5mIikKcmF0aW9zX3NpbmdsZV9wbG90JHNoYXBlIDwtICIxNiIKCnJhdGlvc19zaW5nbGVfaW52YWxpZCA8LSByYXRpb3Nfc2luZ2xlICU+JSAKICBkcm9wX25hKCkgJT4lIAogIGZpbHRlcihgbG9nMihGQylgID09ICItSW5mIiB8IGBsb2cyKEZDKWAgPT0gIkluZiIpCnJhdGlvc19zaW5nbGVfaW52YWxpZCRgbG9nMihGQylgW3JhdGlvc19zaW5nbGVfaW52YWxpZCRgbG9nMihGQylgID09ICItSW5mIl0gPC0gbWluKHJhdGlvc19zaW5nbGVfcGxvdCRgbG9nMihGQylgKSoxLjUKcmF0aW9zX3NpbmdsZV9pbnZhbGlkJGBsb2cyKEZDKWBbcmF0aW9zX3NpbmdsZV9pbnZhbGlkJGBsb2cyKEZDKWAgPT0gIkluZiJdIDwtIG1heChyYXRpb3Nfc2luZ2xlX3Bsb3QkYGxvZzIoRkMpYCkqMS41CnJhdGlvc19zaW5nbGVfaW52YWxpZCRzaGFwZSA8LSAiMTciCgpyYXRpb3Nfc2luZ2xlX3Bsb3QgPC0gZnVsbF9qb2luKHJhdGlvc19zaW5nbGVfcGxvdCwgcmF0aW9zX3NpbmdsZV9pbnZhbGlkKQpgYGAKCmBgYHtyIHNpbmdsZS12b2xjYW5vLXBsb3R9CmdncGxvdChyYXRpb3Nfc2luZ2xlX3Bsb3QsIGFlcyh4ID0gYGxvZzIoRkMpYCwgeSA9IC1sb2cxMChgUCB2YWx1ZWApKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gRGlmZkV4cCwgc2hhcGUgPSBzaGFwZSkpICsKICBleHBhbmRfbGltaXRzKHggPSBjKG1pbihyYXRpb3Nfc2luZ2xlX3Bsb3QkYGxvZzIoRkMpYCksIG1heChyYXRpb3Nfc2luZ2xlX3Bsb3QkYGxvZzIoRkMpYCkpKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYyhoaXN0b25lX0ZDX3RocmVzaG9sZCwgLWhpc3RvbmVfRkNfdGhyZXNob2xkKSwKICAgICAgICAgICAgIGFscGhhID0gMC41LCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKGhpc3RvbmVfcF90aHJlc2hvbGQpLAogICAgICAgICAgICAgYWxwaGEgPSAwLjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gbGFiZWxzKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgbWF4Lm92ZXJsYXBzID0gMjAsCiAgICAgICAgICAgICAgICAgIHNpemUgPSByZWwoMykpICsKICBndWlkZXMoc2hhcGUgPSAibm9uZSIpICsKICBsYWJzKHggPSAibG9nMihGb2xkIENoYW5nZSkiLCB5ID0gIi1sb2cxMChQIHZhbHVlKSIsCiAgICAgICB0aXRsZSA9ICJGaWd1cmUgMy4xYjogTmV0IGhpc3RvbmUgdm9sY2FubyBwbG90IiwKICAgICAgIGNvbG9yID0gIkNhdGVnb3J5IikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoCiAgICBhZXN0aGV0aWNzID0gImNvbG9yIiwgb3B0aW9uID0gIm1hZ21hIiwKICAgIGJlZ2luID0gMC4yLCBlbmQgPSAwLjkKICApCmBgYAoKIyBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbgoKSGVyZSB3ZSBiZWdpbiBhIGNvbXBhcmF0aXZlIGFuYWx5c2lzIG9mIHRoZSB0d28gbGFyZ2VyIGRhdGFzZXRzICh0b3RhbCBwcm90ZW9tZSBhbmQgcGhvc3Bob3Byb3Rlb21lKS4KCmBgYHtyIG92ZXJhbGwtdmVubiwgZmlnLmFzcCA9IDAuN30KIyBWZW5uIGRpYWdyYW0gdG8gcXVpY2tseSBsb29rIGF0IHByb3Rlb21lL3Bob3NwaG9wcm90ZW9tZSBvdmVybGFwCnZlbm5fbGlzdHMgPC0gbGlzdF90b19kYXRhX2ZyYW1lKGxpc3QoIlNldCAxIiA9IHRvdGFsX0tCJFNZTUJPTDIsICJTZXQgMiIgPSBwaG9zcGhvX3JhdyRTWU1CT0wyKSkKZ2d2ZW5uKHZlbm5fbGlzdHMsIHNldF9uYW1lX3NpemUgPSA1KSArCiAgZ2d0aXRsZSgiRmlndXJlIDRhOiBTZXQgb3ZlcmxhcCIpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IG1hcmdpbigxLCAxLCAxLCAyKSkKYGBgCgojIyBUb3RhbCBwcm90ZW9tZSBpbmZlY3RlZCB2cyBjb250cm9sCgpTZWN0aW9uIDQuMSBkaXJlY3RseSBjb21wYXJlcyB0aGUgdG90YWwgcHJvdGVvbWUgdG8gaXRzZWxmLiBIZXJlLCB3ZSBleGFtaW5lIHRoZSBzZXRzIG9mIHByb3RlaW5zIGlkZW50aWZpZWQgZXhjbHVzaXZlbHkgaW4gaW5mZWN0ZWQgY2VsbHMgKGlkZW50aWZpZWQgYnkgY29sdW1ucyB3aXRoICJKTUkiKSwgY29udHJvbCBjZWxscywgb3IgYm90aC4gRGV0ZWN0aW9uIGlucHV0cyBmb3IgdGhlc2UgY29sdW1ucyBjYW4gYmUgIkhpZ2giLCAiUGVhayBGb3VuZCIsIG9yICJuL2EiLiBUbyBpZGVudGlmeSBhIHByb3RlaW4gZm91bmQgb25seSBpbiBvbmUgY29uZGl0aW9uLCB3ZSBjaGVjayB0aGF0IGF0IGxlYXN0IG9uZSByZXBsaWNhdGUgZnJvbSB0aGF0IGNvbmRpdGlvbiBpcyBub3QgIm4vYSIuIFdlIHRoZW4gY2hlY2sgdGhhdCwgc2ltdWx0YW5lb3VzbHksIGFsbCByZXBsaWNhdGVzIGZyb20gdGhlIG90aGVyIGNvbmRpdGlvbiBhcmUgZXF1YWwgdG8gIm4vYSIsIGluZGljYXRpbmcgdGhlIHByb3RlaW4gd2FzIG5vdCBmb3VuZC4gSWYgYm90aCByZXF1aXJlbWVudHMgYXJlIG1ldCwgYSBzdHJpbmcgaXMgbWFwcGVkIHRvIHRoYXQgcHJvdGVpbiBpbmRpY2F0aW5nIGl0cyBwcmVzZW5jZS4gSWYgYSBwcm90ZWluIGhhcyBhdCBsZWFzdCBvbmUgcmVwbGljYXRlIG5vdCBlcXVhbCB0byAibi9hIiBpbiBib3RoIGV4cGVyaW1lbnRhbCBjb25kaXRpb25zLCAiQWxsIiBpcyBhc3NpZ25lZCwgaW5kaWNhdGluZyBvdmVybGFwLgoKYGBge3IgY29uZGl0aW9uLWNhc2VzfQojIElkZW50aWZ5IHByb3RlaW5zIGV4cHJlc3NlZCBpbiBib3RoIGNvbmRpdGlvbnMsIG9ubHkgY29udHJvbCwgb3Igb25seSBpbmZlY3RlZC4KdG90YWxfZGlmZl92YWxpZCA8LSB0b3RhbF9LQiAlPiUgIG11dGF0ZShgRGlmZmVyZW50aWFsP2AgPSBjYXNlX3doZW4oCiAgIyBDb250cm9sIE9OTFkKICAjIEJDRyBlbXB0eSwgYXQgbGVhc3QgMSBjdHJsIG5vdCBlbXB0eQogICgoKGBGb3VuZCBpbiBTYW1wbGU6IEYxOiBTYW1wbGUsIENvbnRyb2xgICE9ICJuL2EiKSB8CiAgICAgIChgRm91bmQgaW4gU2FtcGxlOiBGMjogU2FtcGxlLCBDb250cm9sYCAhPSAibi9hIikgfAogICAgICAoYEZvdW5kIGluIFNhbXBsZTogRjM6IFNhbXBsZSwgQ29udHJvbGAgIT0gIm4vYSIpKSAmCiAgICAgKChgRm91bmQgaW4gU2FtcGxlOiBGNDogU2FtcGxlLCBKTUlgID09ICJuL2EiKSAmCiAgICAgICAgKGBGb3VuZCBpbiBTYW1wbGU6IEY1OiBTYW1wbGUsIEpNSWAgPT0gIm4vYSIpICYKICAgICAgICAoYEZvdW5kIGluIFNhbXBsZTogRjU6IFNhbXBsZSwgSk1JYCA9PSAibi9hIikgJgogICAgICAgIChgRm91bmQgaW4gU2FtcGxlOiBGNjogU2FtcGxlLCBKTUlgID09ICJuL2EiKSAmCiAgICAgICAgKGBGb3VuZCBpbiBTYW1wbGU6IEY3OiBTYW1wbGUsIEpNSWAgPT0gIm4vYSIpICYKICAgICAgICAoYEZvdW5kIGluIFNhbXBsZTogRjg6IFNhbXBsZSwgSk1JYCA9PSAibi9hIikpKSB+ICJDb250cm9sIG9ubHkiLAogICMgSW5mZWN0ZWQgT05MWQogICMgQ29udHJvbCBlbXB0eSwgYXQgbGVhc3Qgb25lIEJDRyBub3QgZW1wdHkKICAgICgoKGBGb3VuZCBpbiBTYW1wbGU6IEYxOiBTYW1wbGUsIENvbnRyb2xgID09ICJuL2EiKSAmCiAgICAgICAgKGBGb3VuZCBpbiBTYW1wbGU6IEYyOiBTYW1wbGUsIENvbnRyb2xgID09ICJuL2EiKSAmCiAgICAgICAgKGBGb3VuZCBpbiBTYW1wbGU6IEYzOiBTYW1wbGUsIENvbnRyb2xgID09ICJuL2EiKSkgJgogICAgICAgKChgRm91bmQgaW4gU2FtcGxlOiBGNDogU2FtcGxlLCBKTUlgICE9ICJuL2EiKSB8CiAgICAgICAgICAoYEZvdW5kIGluIFNhbXBsZTogRjU6IFNhbXBsZSwgSk1JYCAhPSAibi9hIikgfAogICAgICAgICAgKGBGb3VuZCBpbiBTYW1wbGU6IEY1OiBTYW1wbGUsIEpNSWAgIT0gIm4vYSIpIHwKICAgICAgICAgIChgRm91bmQgaW4gU2FtcGxlOiBGNjogU2FtcGxlLCBKTUlgICE9ICJuL2EiKSB8CiAgICAgICAgICAoYEZvdW5kIGluIFNhbXBsZTogRjc6IFNhbXBsZSwgSk1JYCAhPSAibi9hIikgfAogICAgICAgICAgKGBGb3VuZCBpbiBTYW1wbGU6IEY4OiBTYW1wbGUsIEpNSWAgIT0gIm4vYSIpKSkgfiAiQkNHIG9ubHkiLAogICAgIyBCb3RoCiAgICAuZGVmYXVsdCA9ICJBbGwiKSkKYGBgCgpFdWxlciBwbG90cyBhcmUgc2l6ZWQgcHJvcG9ydGlvbmFsbHkgdG8gdGhlaXIgZ3JvdXBzIGFuZCBhcmUgbW9yZSBmcmllbmRseSB0byBtdWx0aXBsZSBzZXRzLiBUbyBnZW5lcmF0ZSB0aGUgRXVsZXIgcGxvdHMsIHRoZSBpbnB1dCBuZWVkcyB0byBiZSBhIHNpbmdsZSBjb2x1bW4gY29udGFpbmluZyBhbGwgcGVwdGlkZXMgZm91bmQgaW4gdGhhdCBjb25kaXRpb24uIFRoZSBgcHJpbnRgIGNvbW1hbmQgcHJvdmlkZXMgc3VtbWFyeSBzdGF0aXN0aWNzIHRvIGVuc3VyZSB0aGF0IHRoZSBFdWxlciBwbG90IGRvZXMgbm90IG1pc3JlcHJlc2VudCB0aGUgZGF0YS4KCmBgYHtyIGV1bGVyLWxpc3RzfQojIFBpdm90aW5nIHRvIG1ha2UgYSB3aWRlciB0YWJsZQp0b3RhbF93aWRlcl92YWxpZCA8LSBwaXZvdF93aWRlcih0b3RhbF9kaWZmX3ZhbGlkLAogIG5hbWVzX2Zyb20gPSBgRGlmZmVyZW50aWFsP2AsIHZhbHVlc19mcm9tID0gYFNZTUJPTDJgKQogIAojIENyZWF0ZSBhIGxpc3Qgb2YgaW5mZWN0aW9uIG9ubHkgcGVwdGlkZXMsIHJlbW92aW5nIGVtcHR5IHJvd3MgKE5BKQpCQ0dfZGlzdF92YWxpZCA8LSB0b3RhbF93aWRlcl92YWxpZCAlPiUgZGlzdGluY3QoYEJDRyBvbmx5YCkgJT4lIGRyb3BfbmEoKQpjb2xuYW1lcyhCQ0dfZGlzdF92YWxpZCkgPC0gYygiUGVwdGlkZXMiKQoKIyBDcmVhdGUgYSBsaXN0IG9mIGNvbnRyb2wgb25seSBwZXB0aWRlcywgcmVtb3ZpbmcgZW1wdHkgcm93cyAoTkEpCkN0cmxfZGlzdF92YWxpZCA8LSB0b3RhbF93aWRlcl92YWxpZCAlPiUgZGlzdGluY3QoYENvbnRyb2wgb25seWApICU+JSBkcm9wX25hKCkKY29sbmFtZXMoQ3RybF9kaXN0X3ZhbGlkKSA8LSBjKCJQZXB0aWRlcyIpCgojIENyZWF0ZSBhIGxpc3Qgb2YgcGVwdGlkZXMgcHJlc2VudCBpbiBib3RoLCByZW1vdmluZyBlbXB0eSByb3dzIChOQSkKQWxsX2Rpc3RfdmFsaWQgPC0gdG90YWxfd2lkZXJfdmFsaWQgJT4lIGRpc3RpbmN0KGBBbGxgKSAlPiUgZHJvcF9uYSgpCmNvbG5hbWVzKEFsbF9kaXN0X3ZhbGlkKSA8LSBjKCJQZXB0aWRlcyIpCgojIENyZWF0ZSBhIGxpc3Qgb2YgQkNHICsgYm90aApCQ0dfYm90aF9kaXN0X3ZhbGlkIDwtIGFkZF9yb3coQkNHX2Rpc3RfdmFsaWQsIEFsbF9kaXN0X3ZhbGlkKSAlPiUgZGlzdGluY3QoKQpjb2xuYW1lcyhCQ0dfYm90aF9kaXN0X3ZhbGlkKSA8LSBjKCJJbmZlY3RlZCIpCgojIENyZWF0ZSBhIGxpc3Qgb2YgY29udHJvbCArIGJvdGgKQ3RybF9ib3RoX2Rpc3RfdmFsaWQgPC0gYWRkX3JvdyhDdHJsX2Rpc3RfdmFsaWQsIEFsbF9kaXN0X3ZhbGlkKSAlPiUgZGlzdGluY3QoKQpjb2xuYW1lcyhDdHJsX2JvdGhfZGlzdF92YWxpZCkgPC0gYygiQ29udHJvbCIpCgojIEdlbmVyYXRlIHBsb3QKZml0X3RvdGFsX3ZhbGlkIDwtIGV1bGVyKGMoQkNHX2JvdGhfZGlzdF92YWxpZCwgQ3RybF9ib3RoX2Rpc3RfdmFsaWQpKQpwbG90KGZpdF90b3RhbF92YWxpZCwKICBxdWFudGl0aWVzID0gVFJVRSwKICBmaWxscyA9IGMoInBhbGV2aW9sZXRyZWQxIiwgImNvcm5mbG93ZXJibHVlIiwgImdhaW5zYm9ybyIpLAogIG1haW4gPSAiRmlndXJlIDQuMWE6IENvbmRpdGlvbiBFdWxlciBwbG90IgopCmBgYAoKYGBge3J9CnByaW50KGZpdF90b3RhbF92YWxpZCkKYGBgCgojIERpc2Vhc2UgZW5yaWNobWVudAoKVG8gY29tcGxldGUgdGhlIGludGVncmF0ZWQgYW5hbHlzaXMsIHdlIGV4YW1pbmUgYSBnZW5lLWRpc2Vhc2UgYXNzb2NpYXRpb24gKEdEQSkgc3VtbWFyeSBmcm9tIERpc0dlTkVULiBUaGUgR0RBIEV4Y2VsIHNoZWV0cyBjYW4gYmUgZG93bmxvYWRlZCBkaXJlY3RseSBmcm9tIHRoZSB3ZWJzaXRlIGFmdGVyIGlkZW50aWZ5aW5nIHRoZSBkaXNlYXNlIG9mIGludGVyZXN0LiBQb2ludCB0aGUgYHJlYWRfeGxzeGAgY2FsbCB0byB0aGlzIGZpbGUuCgpgYGB7ciBHREEsIGZpZy5hc3AgPSAxfQpCQ194bCA8LSByZWFkX3hsc3goIn4vRG9jdW1lbnRzL1IvRmluYWwgYW5hbHlzaXMvQzIxNDU0NzJfZGlzZWFzZV9nZGFfc3VtbWFyeS54bHN4IikKCiMgR2V0IGxpc3RzIG9mIHByb3RlaW5zIHRoYXQgd2VyZSBzaWduaWZpY2FudCBBTkQgYWJvdmUgdGhlIEZDIHRocmVzaG9sZAp0b3RhbF9GQyA8LSBmaWx0ZXIodG90YWxfc2lnbmlmaWNhbnQsIGFicyhgbG9nMihGQylgKSA+IHRvdGFsX0ZDX3RocmVzaG9sZCkKcGhvc3Bob19GQyA8LSBmaWx0ZXIocGhvc3Bob19zaWduaWZpY2FudCwgYWJzKGBsb2cyKEZDKWApID4gcGhvc3Bob19GQ190aHJlc2hvbGQpCgojIEdlbmVyYXRlIHBsb3QKZ2d2ZW5uKGxpc3QoCiAgIk5NSUJDIiA9IEJDX3hsJEdlbmUsCiAgIlRvdGFsIHByb3Rlb21lIiA9IHRvdGFsX0ZDJFNZTUJPTDIsCiAgIlBob3NwaG9wcm90ZW9tZSIgPSBwaG9zcGhvX0ZDJFNZTUJPTDIKKSkgKyBnZ3RpdGxlKCJGaWd1cmUgNWE6IFZlbm4gZGlhZ3JhbSIpICsgdGhlbWUoCiAgcGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwKICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKZml0X0JDIDwtIGV1bGVyKGxpc3QoCiAgIk5NSUJDIiA9IEJDX3hsJEdlbmUsCiAgIlRvdGFsIHByb3Rlb21lIiA9IHRvdGFsX0ZDJFNZTUJPTDIsCiAgIlBob3NwaG9wcm90ZW9tZSIgPSBwaG9zcGhvX0ZDJFNZTUJPTDIKKSkKcGxvdChmaXRfQkMsIHF1YW50aXRpZXMgPSBUUlVFLCBtYWluID0gIkZpZ3VyZSA1YTogR0RBIEV1bGVyIHBsb3QiKQpgYGAKCmBgYHtyfQpwcmludChsaXN0X3RvX2RhdGFfZnJhbWUoZml0X0JDKSkKYGBgCgpGcm9tIHRoaXMgdGFibGUsIHRoZXJlIGlzIGEgbWlzc2luZyBvdmVybGFwIHJlZ2lvbiBpbiBhbGwgdGhyZWUgc2V0cyB0aGF0IHRoZSBFdWxlciBkaWFncmFtIGNvdWxkIG5vdCByZXNvbHZlLCBhcyBpbmRpY2F0ZWQgYnkgdGhlIG5vbi16ZXJvIHJlc2lkdWFscy4gSW4gdGhpcyBjYXNlLCB0aGUgVmVubiBkaWFncmFtIGlzIHRoZSBiZXR0ZXIgY2hvaWNlLgoKYGBge3IgR0RBLWxpc3QsIHJlc3VsdHMgPSAiYXNpcyJ9CiMgUHJpbnQgdGhlIGxpc3Qgb2YgcHJvdGVpbnMgaW4gZWFjaCBpbnRlcnNlY3Rpb24KY2F0KHBhc3RlKCIqKk5NSUJDL1Bob3NwaG86KiogIiwgcGFzdGUoaW50ZXJzZWN0KEJDX3hsJEdlbmUsIHBob3NwaG9fRkMkU1lNQk9MMiksIGNvbGxhcHNlID0gIiwgIikpKQpjYXQocGFzdGUoIioqTk1JQkMvVG90YWw6KiogIiwgcGFzdGUoaW50ZXJzZWN0KEJDX3hsJEdlbmUsIHRvdGFsX0ZDJFNZTUJPTDIpLCBjb2xsYXBzZSA9ICIsICIpKSkKY2F0KHBhc3RlKCIqKlRvdGFsL1Bob3NwaG86KiogIiwgcGFzdGUoaW50ZXJzZWN0KHRvdGFsX0ZDJFNZTUJPTDIsIHBob3NwaG9fRkMkU1lNQk9MMiksIGNvbGxhcHNlID0gIiwgIikpKQpjYXQocGFzdGUoIioqTk1JQkMvUGhvc3Boby9Ub3RhbDoqKiAiLCBwYXN0ZShpbnRlcnNlY3QoaW50ZXJzZWN0KEJDX3hsJEdlbmUsIHBob3NwaG9fRkMkU1lNQk9MMiksIHRvdGFsX0ZDJFNZTUJPTDIpLCBjb2xsYXBzZSA9ICIsICIpKSkKYGBg